透过源码学习CoordinatorLayout与Behavior
CoordinatorLayout(协调布局)是将嵌套滑动的泛用性发挥到极致的控件,只需要通过简单配置它的子View的Behavior(行为)就可以实现复杂而炫丽的效果。
Behavior
Behavior是CoordinatorLayout的内部静态类,通过继承Behavior可以实现自定义的行为。
1 | public static abstract class Behavior<V extends View> { |
以上源码摘录了Behavior常用的方法,从简单到复杂的需求皆可实现。简单需求可以搭配layoutDependsOn和onDependentViewChanged,复杂需求可自行定制嵌套滑动的细节。
Behavior的绑定方式
通过布局属性绑定
即在布局文件中通过app:layout_behavior添加,看看源码是怎么解析的:
1 | static Behavior parseBehavior(Context context, AttributeSet attrs, String name) { |
可以看到,在布局文件中通过填写的全名或者半名自动补全查找,然后通过反射实例化对应的Behavior类。接下来再来看看,这个方法在哪里调用的:
1 | public static class LayoutParams extends MarginLayoutParams { |
可以看到,Behavior是在LayoutParams初始化时解析并绑定的。
绑定默认行为
旧版本的默认行为是在自定义的类上添加注解**@DefaultBehavior(),而新版本则要求自定义类实现AttachedBehavior接口,并通过getBehavior()**方法返回。
1 | LayoutParams getResolvedLayoutParams(View child) { |
Behavior与行为交互
CoordinatorLayout的本质还是通过Behavior将嵌套滑动“发散”出去,“分给”一个或多个子View同步享用。要知道它是怎么做到的,只要看看它其中的与嵌套滑动相关的方法即可,例如onStartNestedScroll:
1 |
|
onNestedScroll:
1 |
|
在onNestedScroll方法中可以明显的看到,如果有其中任意一个Child的Behavior接收并消费了事件,那么就会在方法结束位置调用onChildViewChanged方法,onChildViewChanged顾名思义——有View发生了改变,很容易就可以联想到一定会触发某个Behavior的onDependentViewChanged方法(如果有View依赖这个消费事件的View的情况下)。
为了印证这种想法,就需要查看onChildViewChanged:
1 |
|
果不其然,在倒数几行的位置找到了handled = b.onDependentViewChanged(this, checkChild, child),整个流程就已经连起来了。
CoordinatorLayout
还记得刚才说Behavior绑定的时候找到的getResolvedLayoutParams吗,那么它是在哪里调用的呢,继续找,找到如下代码:
1 | private void prepareChildren() { |
然后,在onMeasure方法中调用,准备好所有的子节点,然后再开始测量,接着再进行布局。对于所有节点,都会尝试优先查找节点的LayoutParams是否包含Behavior,如果包含,再询问Behavior是否接管测量或者布局(即behavior.onMeasureChild和behavior.onLayoutChild)。