From Punched Cards to Prompts
AndroidIntroduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
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!
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.
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.
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.
Introduction When computer programming was young, code was punched into cards. That is, holes were punched into a piece of cardboard in a format...
Jetpack Compose is a declarative framework for building native Android UI recommended by Google. To simplify and accelerate UI development, the framework turns the...
Big Nerd Ranch is chock-full of incredibly talented people. Today, we’re starting a series, Tell Our BNR Story, where folks within our industry share...