I bring to your attention a translation of the article Ian Lake Intercepting everything with CoordinatorLayout Behaviors .You will not go far in learning
Android Design Support Library without encountering
CoordinatorLayout . Many View from Design Library require CoordinatorLayout. But why? The CoordinatorLayout itself does not do much, if you use it with the View, which is part of the Android framework, it will work like a regular FrameLayout. So where does all his magic come from? This is where
CoordinatorLayout.Behavior comes on the scene. By connecting Behavior to a child View of the CoordinatorLayout, you can intercept touches, window inserts (window insets), resize and layout (measurement and layout), and also nested scrolling. Design Library makes extensive use of Behavior to add strength to most of the functionality you see.

Create Behavior
Creating Behavior is quite simple: inheriting our class from Behavior:
')
Behavior creation examplepublic class FancyBehavior<V extends View> extends CoordinatorLayout.Behavior<V> { public FancyBehavior() { } public FancyBehavior(Context context, AttributeSet attrs) { super(context, attrs);
Note the generic type specified in this class. In this case, we indicate that we can connect FancyBehavior to any View. However, if you want to allow your Behavior to be connected to a specific type of View, you can write this:
public class FancyFrameLayoutBehavior extends CoordinatorLayout.Behavior<FancyFrameLayout>
This can save you from casting a large number of parameters in methods to the subtype of View you need - simple and convenient.
There are methods to save both the temporary data of
Behavior.setTag () /
Behavior.getTag () and the state of the Behavior instance using
onSaveInstanceState () /
onRestoreInstanceState () . I urge you to create Behavior as easy as possible, but these methods allow you to create Behavior with the ability to save state.
Behavior connection
Of course, Behavior does not do anything on its own, so that we can use it, it must be connected to a CoordinatorLayout's child View. There are three main ways to do this: programmatically, in XML or automatically using annotation.
Behavior software connection
When you think of Behavior as something additionally connected to each View inside the CoordinatorLayout, you should not be surprised (if you read
our post about layouts ) that
Behavior is actually stored in the LayoutParams of each View - which is why Behavior should be declared the View is inside the CoordinatorLayout, since only this View has a specific LayoutParams subtype that Behavior can store.
FancyBehavior fancyBehavior = new FancyBehavior(); CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) yourView.getLayoutParams(); params.setBehavior(fancyBehavior);
As you can see, in this case we use the usual empty constructor. But this does not mean that you cannot create a constructor that takes as many parameters as you want. When you do something through a code, there is no limit to possibilities.
Connecting Behavior via XML
Of course, if you constantly do everything through the code - it can cause confusion. As with most custom LayoutParams, there is an appropriate layout_ attribute to do the same. In our case, this is the
layout_behavior attribute:
<FrameLayout android:layout_height=”wrap_content” android:layout_width=”match_parent” app:layout_behavior=”.FancyBehavior” />
Here, unlike the software method, the constructor of
FancyBehavior (Context context, AttributeSet attrs) will always be called. But, as a bonus, you can declare any other user attributes and extract them from the XML AttributeSet, this is important if you want (and you want) to allow other developers to customize the functionality of your Behavior via XML.
Note : Similarly, with the agreement of the name of the layout_ attribute, which the parent class must be able to analyze and understand, use the
prefix for the attribute_ for any attributes used within Behavior.
Automatic connection Behavior
If you create your View, which needs your Behavior (as was the case with most components in the Design Library), then you most likely want to connect Behavior by default, without permanent manual connection via code or XML. To do this, you just need to add a simple annotation on top of the class of your View:
@CoordinatorLayout.DefaultBehavior(FancyFrameLayoutBehavior.class) public class FancyFrameLayout extends FrameLayout { }
You will notice that your Behavior will be invoked using a standard empty constructor, making this method similar with the Behavior software connection. Note that any
layout_behavior attribute
will override the
DefaultBehavior .
Interception touch
As soon as you connect Behavior, you are ready for action. One of Behavior's abilities is touch interception.
Without a
CoordinatorLayout , this usually involved subclasses of each ViewGroup, as stated in the
Managing Touch Events training . However, in the case of the
CoordinatorLayout , it will transfer the
onInterceptTouchEvent () method calls to the
onInterceptTouchEvent () method of your Behavior,
allowing your Behavior to intercept the touches . If you return
true to this method, your Behavior will get all subsequent touches using the
onTouchEvent () method - all this happens in secret from View. For example, this is
how SwipeDismissBehavior works with any View.
There is another, more complicated case of intercepting touches - blocking any interaction. It is enough to return
true in the
BlocksInteractionBelow () method. Most likely, you will want to somehow visually show that the interaction is blocked (so that users do not think that the application is broken) - this is why the standard functional
blocksInteractionBelow () depends on the value of
getScrimOpacity () . Returning a value not equal to zero, at once we draw a color (
getScrimColor () , black by default) over the View and
turn off the touch interaction. Conveniently.
Interception of window inserts
Let's say you read the blog
Why would I want to fitsSystemWindows? . There we discussed in detail what
fitsSystemWindows does, but it all came down to representing window inserts (window insets), necessary to avoid drawing under system windows (such as status bar and navigation bar). Behavior can prove himself here. If your View
fitsSystemWindows = “true” , then any connected Behavior will receive a call to the
onApplyWindowInsets () method, in priority over the View itself.
Note : in most cases, if your Behavior does not handle window inserts entirely, it must pass these inserts using
ViewCompat.dispatchApplyWindowInsets () to make sure that all child View will be able to see WindowInsets.
Interception Measurement and Layout
These are the two key components in how
Android draws View . Therefore, it makes sense that Behavior, as an interceptor of all events, will also be the first to receive notification of resizing and layout by calling the
onMeasureChild () and
onLayoutChild () methods.
For example, let's take any generic ViewGroup and add maxWidth to it. As shown in the
MaxWidthBehavior.java class.
Creating a generic Behavior that can work with any View is very convenient. But remember that you can simplify your life by considering whether Behavior will be used only inside your application (not all Behavior should be generic!).
Understanding the dependencies between the View
Everything described above requires a single View. But the real power of Behavior manifests itself in building dependencies between the View, that is, when a View changes, your Behavior receives a notification about this and changes its functionality depending on external conditions.
Behavior can make View dependent on each other in two different ways: when View is connected (anchor) to another View (implied dependency) or when you explicitly return
true in the
layoutDependsOn () method.
A binding occurs when your View inside the
CoordinatorLayout uses the
layout_anchor attribute. This attribute, combined with
layout_anchorGravity , allows you to effectively link the position of two View together. For example, you can associate
FloatingActionButton with
AppBarLayout , then
FloatingActionButton.Behavior will use an implicit dependency to hide the FAB if
AppBarLayout is scrolled off the screen.
In any case, your
Behavior will receive calls to the
onDependentViewRemoved () method when the dependent View has been deleted and
onDependentViewChanged () whenever the dependent View has changed (its size or position has changed).
Most of the cool functionality of the Design Library works because of the possibility of bundling the View together. Take for example the interaction between
FloatingActionButton and
Snackbar .
The FAB
Behavior depends on the
Snackbar instances added to the
CoordinatorLayout , then, using the
onDependentViewChanged () method call, moves the FAB higher to prevent the
Snackbar from overlapping.
Note : when you add a dependency, the View will always be placed after the dependent View, regardless of the sequence in the markup.
Nested scrolling
Oh, nested scrolling. I just touch this topic in a post. You need to keep in mind about several things:
- You should not declare dependencies on the View with nested scrolling. Each View inside the CoordinatorLayout can receive nested scroll events.
- Nested scrolling can occur not only for the View inside the CoordinatorLayout , but also for any child View (for example, the “child” of the “child” of the “child” of the CoordinatorLayout ).
- I will call this nested scrolling, but in fact it will be understood as just scrolling and fast scrolling (fling).
Nested scrolling events begin with the
onStartNestedScroll () method. You will get the scroll axes (horizontal or vertical - we can easily ignore the scrolling in a certain direction) and
must return true to receive further scroll events .
After you return
true in the
onStartNestedScroll () method, nested scrolling works in two steps:
- onNestedPreScroll () is called before the scrolling View received a scroll event and allows your Behavior to process some or all of the scrolling (the last int [] parameter is the outgoing parameter where you can specify which part of the scroll Behavior has processed).
- onNestedScroll () is called as soon as the scrolling View has been scrolled. You will get the value of how much the View has been scrolled and the raw values.
There are similar methods for fast scrolling (but the pre-fling processing method should handle either all the scrolling, or not at all).
When nested (or fast) scrolling ends, you get a call to the
onStopNestedScroll () method. This marks the end of the scrolling and waiting for a new call to the
onStartNestedScroll () method before the new scrolling begins.
Take for example the occasion when you want to hide the
FloatingActionButton when scrolling down and show FAB when scrolling up. To do this, override the
onStartNestedScroll () and
onNestedScroll () methods, as seen in
ScrollAwareFABBehavior .
And this is just the beginning.
If every single part of
Behavior is only interesting, then when they are all together, magic begins. I highly recommend viewing the Source Library Design Library for more features. The Chrome
Android SDK Search extension is still one of my favorite resources for researching the Android Open Project AOSP code. In addition, you can find the latest versions of the source code along the path
<android-sdk> / extras / android / m2repository .
Let me know how you use the basics on what Behavior hashtag #BuildBetterApps can do.
Join the discussion on
Google+ and subscribe to the
Android Development Patterns Collection for even more information!