Upcoming and OnDemand Webinars View full list

Customizing CoordinatorLayout’s Behavior

Chris Stewart

When the Android design support library dropped a few months ago, developers were given all kinds of goodies. Most of these goodies are self-explanatory: The floating action button, you’ve seen that. Snackbars and Tabs? Everyone knows what those are.

But there is also something mysterious lurking in the design support library. Something that has its tentacles throughout. This mysterious thing is known as the CoordinatorLayout.

CoordinatorLayout

You don’t hear much about CoordinatorLayout, probably because when you use it, you generally don’t need to know many of the details. Things just work and they just work well. However, CoordinatorLayout is more powerful than it first seems.

Take a snackbar and a floating action button as an example. Using a CoordinatorLayout, you can ensure that your floating action button will move out of the way when the snack bar pops in.

Just throw a CoordinatorLayout in your layout file:

<android.support.design.widget.CoordinatorLayout
  android:id="@+id/container"
  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.design.widget.FloatingActionButton
    android:id="@+id/fab"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="bottom|right|end"
    android:layout_margin="8dp"
    android:src="@drawable/abc_ic_search_api_mtrl_alpha"/>

</android.support.design.widget.CoordinatorLayout>

Then show your snack bar:

Snackbar.make(findViewById(R.id.container), "Hey there!", Snackbar.LENGTH_LONG).show();

Magically, your floating action button will move when the snack bar shows up. That’s it!

Moving Floating Action Button

Out of the box, CoordinatorLayout can also increase the size of your toolbar based on scroll position or hide your toolbar when the user is scrolling down a list. You can customize it with your own behavior, too.

So how does CoordinatorLayout know what to do with the floating action button? How does it know that it should move up the screen when the snack bar comes in?

CoordinatorLayout makes this happen by making use of a CoordinatorLayout.Behavior implemented and declared by FloatingActionButton.

CoordinatorLayout.Behavior

Views within a coordinator layout can specify a Behavior that defines how that view interacts with other views.

@CoordinatorLayout.DefaultBehavior(FloatingActionButton.Behavior.class)
public class FloatingActionButton extends ImageView {
  ...
}

If you look at the source for FloatingActionButton, you will see its Behavior defined with an annotation on the class declaration.

FloatingActionButton uses FloatingActionButton.Behavior as its default behavior unless you set it to something else. The default knows how to get out of the way of the snackbar when it shows up.

Custom Behavior

So, how do you customize this behavior? You define your own Behavior subclass.

Let’s create a Behavior that shrinks the FloatingActionButton instead of moving it up the screen when the snackbar shows up.

Shrink the Floating Action Button

First, in your layout file, use the app:layout_behavior attribute to point to your Behavior subclass:

...

<android.support.design.widget.FloatingActionButton
  android:id="@+id/fab"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:layout_gravity="bottom|right|end"
  android:layout_margin="8dp"
  android:src="@drawable/abc_ic_search_api_mtrl_alpha"
  app:layout_behavior="com.bignerdranch.android.custombehavior.ShrinkBehavior"/>

...

Then, create your class:

public class ShrinkBehavior extends CoordinatorLayout.Behavior<FloatingActionButton> {

  public ShrinkBehavior(Context context, AttributeSet attrs) {
      super(context, attrs);
  }

}

When you define your own behavior, there are many methods that you can override. For this example, there are two that are necessary:

@Override
public boolean layoutDependsOn(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
    return dependency instanceof Snackbar.SnackbarLayout;
}

The layoutDependsOn method is CoordinatorLayout’s way to see which views your floating action button are dependent on. In this case, if the view is a snackbar, set up a dependency by returning true.

The Android documentation indicates that by setting up this dependency, you will receive calls to onDependentViewChanged when a dependent view changes its size or position. This is true, but CoordinatorLayout’s source provides more detail. onDependentViewChanged is called whenever ViewTreeObserver.OnPreDrawListener.onPreDraw is called or when a scroll or fling action occurs.

@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, FloatingActionButton child, View dependency) {
  float translationY = getFabTranslationYForSnackbar(parent, child);
  float percentComplete = -translationY / dependency.getHeight();
  float scaleFactor = 1 - percentComplete;

  child.setScaleX(scaleFactor);
  child.setScaleY(scaleFactor);
  return false;
}

As the snackbar moves up the screen, this method is repeatedly called. You can do a little math to determine how far up the screen the snackbar is and change your floating action button’s size as a result. You can see the full implementation of this ShrinkBehavior class on GitHub. You can find the source for this sample and another custom Behavior that rotates the floating action button there as well.

Not Happy with Your Current App, or Digital Product?

Submit your event

Let's Discuss Your Project

Let's Discuss Your Project