Hide The FloatingActionButton When Scrolling A RecyclerView

One of my favorite components introduced with Material Design is the FloatingActionButton (FAB). These buttons are great for emphasizing the primary action of an Activity, but quickly become a nuisance when displayed over a RecyclerView as they may block the bottom list item. To avoid this, we can hide the FloatingActionButton when scrolling a RecyclerView.

In today’s tutorial we will only be hiding the FAB when the RecyclerView is scrolled upward, and it will reappear on the next down scroll as shown here:

FloatingActionButton

Add the support library dependency

As with any support library component, like the Toolbar in my last post, we need to add a dependency to our build.gradle file for the design library, and for the RecyclerView:

	dependencies{
	   compile 'com.android.support:design:23.0.0'
	   compile 'com.android.support:recyclerview-v7:23:0:0'
	}

Add the FAB and RecyclerView inside a CoordinatorLayout

In order to achieve this scroll animation, we must put the Views inside of a CoordinatorLayout. As the documentation states, a primary use of the CoordinatorLayout is:

As a container for a specific interaction with one or more child views

Since the FAB is interacting with the RecyclerView (by hiding/showing depending on scroll) the CoordinatorLayout will help us with this process. Here is how the XML will look:

	<android.support.design.widget.CoordinatorLayout 
	   xmlns:android="http://schemas.android.com/apk/res/android" 
	   xmlns:app="http://schemas.android.com/apk/res-auto" 
	   android:layout_width="match_parent" 
	   android:layout_height="match_parent">  
	 
	   <android.support.v7.widget.RecyclerView 
	      android:id="@+id/recycler_view" 
	      android:layout_width="match_parent" 
	      android:layout_height="match_parent"/>  
	 
	   <android.support.design.widget.FloatingActionButton
	      android:id="@+id/fab"
	      android:layout_width="wrap_content" 
	      android:layout_height="wrap_content" 
	      android:layout_margin="16dp" 
	      app:layout_anchor="@+id/recycler_view" 
	      app:layout_anchorGravity="bottom|end" />
	 
	</android.support.design.widget.CoordinatorLayout>

Two attributes that may be new to you are layout_anchor and layout_anchorGravity. These attributes were introduced with the CoordinatorLayout to place some views relative to others.

Implement the FAB’s behavior

If you took a peak at the CoordinatorLayout documentation, you will notice the mentioned something about Behavior:

By specifying Behaviors for child views of a CoordinatorLayout you can provide many different interactions within a single parent and those views can also interact with one another.

There is a FloatingActionButton.Behavior class that we can extend to implement our own behavior. Start by creating the following class:

	public class FABScrollBehavior extends FloatingActionButton.Behavior { }

In order for our FAB to react to the scroll of the RecyclerView, we can override onStartNestedScroll() and onNestedScroll():

	public class FABScrollBehavior extends FloatingActionButton.Behavior {  
	   @Override  
	   public void onNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed) { 
	      super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed); 
	      if(dyConsumed > 0 && child.getVisibility() == View.VISIBLE){ 
	         child.hide(); 
	      } else if(dyConsumed < 0 && child.getVisibility() == View.GONE){ 
	         child.show(); 
	      }  
	   }  
	 
	   @Override 
	   public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, FloatingActionButton child, View directTargetChild, View target, int nestedScrollAxes) { 
	      return nestedScrollAxes == ViewCompat.SCROLL_AXIS_VERTICAL; 
	   }
	}

This implementation is pretty straight forward. If the scroll is upward (dyConsumed > 0) and the child (the FAB here) is visible, hide it. If the opposite occurs, show it. Thankfully, we do not need to write any animations ourself! The FAB class contains methods for showing and hiding the button.

We’ve also implemented onStartNestedScroll to show that we are only handling vertical scrolls.

Attach the behavior to the FAB

Next we need to make sure the FAB behaves like we just implemented. This can be done using XML:

	<android.support.design.widget.FloatingActionButton 
	   android:layout_width="wrap_content" 
	   android:layout_height="wrap_content" 
	   android:layout_margin="16dp" 
	   app:layout_anchor="@+id/recycler_view" 
	   app:layout_anchorGravity="bottom|end" 
	   app:layout_behavior="com.adammcneilly.fabscrollsample.FABScrollBehavior"/>

While writing this tutorial for you, I learned the hard way that this is not enough. As it is you will get the following error:

Caused by: java.lang.RuntimeException: Could not inflate Behavior subclass com.adammcneilly.fabscrollsample.FABScrollBehavior

To fix this, just make sure you’ve added a default constructor to your behavior class:

	public FABScrollBehavior(Context context, AttributeSet attributeSet){
	   super();
	}

Once you’ve done that, you’ll be able to hide the FloatingActionButton when scrolling the RecyclerView. Great work!

The code for this tutorial can be found on GitHub.

Adam McNeilly

Adam McNeilly
Software Engineer and Android development enthusiast.

Breaking the Buzzwords Barrier Part 3: ViewModel

Explains what a ViewModel means in both MVVM and Android contexts. Continue reading