CoodinatorLayout并不知道FloatingActionButton和AppBarLayout的工作原理,在CoordinatorLayout的基本使用提到CoodinatorLayout实现了NestedScrollingParent,通过一个实现了NestedScrollingChild的scrolling view,就可以轻松的实现滑动事件的处理与View之间的交互。这其中充当中间桥梁的就是CoordinatorLayout.Behavior,比如FloatingActionButton,查看源码发现它的类注解是这样的:
1 | .class) .DefaultBehavior(FloatingActionButton.Behavior |
FloatingActionButton.Behavior 的主要作用就是防止被Snackbar盖住。
自定义View既可以通过注解指定Behavior,也可以通过在布局XML申明:
1 | app:layout_behavior="具体Behavior的类路径" |
Behavior主要功能有三块:
onLayoutChild和onMeasureChild
跟布局有关的,这部分控制界面位置和大小layoutDependsOn和onDependentViewChanged
跟别的子view的位置互动 layoutDependsOn和onDependentViewChangedonXXXSroll和onXXXFling
跟嵌套滑动有关 onStartNestedScroll onNestedPreScroll onNestedScroll onStopNestedScroll onNestedPreFling onNestedFling
onMeasureChild和onLayoutChild
onMeasureChild
1 | public boolean onMeasureChild(@NonNull CoordinatorLayout parent, @NonNull V child, |
这个方法用来计算child的宽高(getMeasuredWidth和getMeasuredHeight),这部分和view的绘制方法和作用都是一样的。
1 | int newHeightMeasureSpec = View.MeasureSpec.makeMeasureSpec(300, View.MeasureSpec.EXACTLY); |
如上,我们重新设定parent的宽高是500*300,然后用新的参数测量child:
1 | parent.onMeasureChild(child, newWidthMeasureSpec, widthUsed, newHeightMeasureSpec, heightUsed); |
输出日志如下:
1 | com.wy521angel.coordinatorlayouttest I/MeasureBehavior: onMeasureChild==========w=500 h=300 |
布局如下:
1 | <View |
就像下图的蓝色的方块,即使在xml文件设定的宽高是match_parent,也是显示成代码里面设置的500*300。

onLayoutChild
1 | public boolean onLayoutChild(@NonNull CoordinatorLayout parent, @NonNull V child, |
这个方法是用来放置child的位置的:
1 |
|
像上面设置之后,效果是Behavior的测量与布局图中的红色方块,往下移动500。
另外,onInterceptTouchEvent和onTouchEvent这两个功能和view的onInterceptTouchEvent和onTouchEvent是一样的,在这两个方法里面进行一些事件的分发,返回true就可以代替view自己的两个方法。这个部分的内容其实和View的绘制和事件分发是一样的。
layoutDependsOn和onDependentViewChanged
这是Behavior比较核心的一个功能。
Behavior可以让一个child根据另外的一个child的位移,来改变自己的状态,无论是位置还是透明度等一些属性。
1 | public boolean layoutDependsOn(@NonNull CoordinatorLayout parent, @NonNull V child, |
这个方法主要是决定当前child要根据哪个child(即dependency)来改变自己,可以用dependency的id、tag、类型等一些办法来判定是否是dependency。
类似ScrollingViewBehavior就是根据view的类型来判断的,如下
1 |
|
当确定好dependency,就可以用下面方法根据dependency位置变化来改变自己的状态了:
1 | public boolean onDependentViewChanged(@NonNull CoordinatorLayout parent, @NonNull V child, |
代码如下:
1 |
|
红色方块会跟着绿色方块移动,如下图:

onXXXXXXXScroll
这个部分也是Behavior比较核心的一个功能,关于这部分的原理解释起来会经常涉及到嵌套滑动,详见嵌套滑动与冲突解决
onXXXXXXXFling部分跟Scroll流程是类似的,此处省略。
CoordinatorLayout实现了NestedScrollingParent就能接收实现了NestedScrollingChild接口的child的滑动事件,它接收到滑动事件后就会把事件发送给每个愿意接收这个事件的child,至于怎么确定是否愿意,是在这个方法onStartNestedScroll:
1 | public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes) { |
只要返回true就表示这个child愿意接收这个滑动事件。如果愿意接收这个滑动事件,就会在接下来的这两个方法onNestedPreScroll、onNestedScroll接收到事件。
这里先简单介绍一下这两个方法的流程,当一个NestedScrollingChild的childA发生滑动的时候,会先询问有没有childB愿意接收这个滑动事件,愿意的话在onStartNestedScroll返回true,然后把这个事件会发给childB的onNestedPreScroll。
1 | public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, |
在onNestedPreScroll里面,childB有可能会消费这个滑动事件,也有可能一点都不消费,或者消费部分,这个都是通过consumed来传回的。
当childB的onNestedPreScroll结束后,childA根据consumed的值,判断是否还有剩下滑动距离没消费完。如果还有的话,childA就开始自己的滑动,当然childA依然还是有可能消费不完这次的滑动事件,比如滑到底部了,childA会再次把事件发给childB的onNestedScroll。
1 | public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, |
如果childB还没消费完这个事件,会再返回到childA,自生自灭。
看如下效果图:

有个NestedScrollView和一个方块,运行规则是这个方块会根据NestedScrollView的反方向移动,当触顶或者触底的时候,才会轮到NestedScrollView自己滚动。consumed控制着方块的消费情况。
1 | public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull View child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) { |
Behavior的更多使用请看CoordinatorLayout之Behavior的相关实例,代码详见CoordinatorLayoutTest
参考资料:
zhuhf CoordinatorLayout 完全解析
Xugter CoordinatorLayout系列(二):Behavior的使用