3.1.3_CoordinatorLayout原理

给子View设置Behavior的原理
如果我们在给CoordinatorLayout设置app:layout_behavior属性

app:layout_behavior="@string/hide_bottom_view_on_scroll_behavior"

CoordinatorLayout会去获取layout_behavior属性
this.mBehaviorResolved = a.hasValue(styleable.CoordinatorLayout_Layout_layout_behavior); if (this.mBehaviorResolved) { this.mBehavior = CoordinatorLayout.parseBehavior(context, attrs, a.getString(styleable.CoordinatorLayout_Layout_layout_behavior)); }

如果layout_behavior属性存在,则会进行解析
static CoordinatorLayout.Behavior parseBehavior(Context context, AttributeSet attrs, String name) { String fullName; if (name.startsWith(".")) { //如果没有加报名,自动添加报名 fullName = context.getPackageName() + name; } else if (name.indexOf(46) >= 0) { fullName = name; } else { fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME) ? WIDGET_PACKAGE_NAME + '.' + name : name; }try { Map> constructors = (Map)sConstructors.get(); if (constructors == null) { constructors = new HashMap(); sConstructors.set(constructors); }Constructor c = (Constructor)((Map)constructors).get(fullName); if (c == null) { //反射加载Behavior Class clazz = context.getClassLoader().loadClass(fullName); c = clazz.getConstructor(CONSTRUCTOR_PARAMS); c.setAccessible(true); ((Map)constructors).put(fullName, c); }return (CoordinatorLayout.Behavior)c.newInstance(context, attrs); } catch (Exception var7) { throw new RuntimeException("Could not inflate Behavior subclass " + fullName, var7); } }

可以看到,会通过反射来加载layout_behavior,并存储到constructors这个Map中
CoordinatorLayout如何结合Behavior实现对子View的事件响应
父View(CoordinatorLayout)实现NestedScrollingParent2接口,子View(Recyclerview)实现NestedScrollingChild2接口
然后这两个接口的实现会交由NestedScrollingChildHelper来实现
来看下Recyclerview,就有如下代码
private NestedScrollingChildHelper getScrollingChildHelper() { if (this.mScrollingChildHelper == null) { this.mScrollingChildHelper = new NestedScrollingChildHelper(this); }return this.mScrollingChildHelper; }public boolean startNestedScroll(int axes) { return this.getScrollingChildHelper().startNestedScroll(axes); }

再来看NestedScrollingChildHelper
public boolean startNestedScroll(int axes, int type) { if (this.hasNestedScrollingParent(type)) { return true; } else { if (this.isNestedScrollingEnabled()) { ViewParent p = this.mView.getParent(); for(View child = this.mView; p != null; p = p.getParent()) { //调用父类的onStartNestedScroll -> 比如CoordinatorLayout 会遍历子View,获得子View的Behavior,调用Behavior的onStartNestedScroll方法 if (ViewParentCompat.onStartNestedScroll(p, child, this.mView, axes, type)) { this.setNestedScrollingParentForType(type, p); //调用父类onNestedScrollAccepted ViewParentCompat.onNestedScrollAccepted(p, child, this.mView, axes, type); return true; }if (p instanceof View) { child = (View)p; } } }return false; } }

可以看到,最终会遍历子View,获得子View的Behavior,调用Behavior的onStartNestedScroll和onNestedScrollAccepted方法
同理,Recyclerview的dispatchNestedScroll通过NestedScrollingChildHelper也会调用onNestedScroll方法
来看NestedScrollingChildHelper#dispatchNestedScroll
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, @Nullable int[] offsetInWindow, int type) { if (this.isNestedScrollingEnabled()) { ViewParent parent = this.getNestedScrollingParentForType(type); if (parent == null) { return false; }if (dxConsumed != 0 || dyConsumed != 0 || dxUnconsumed != 0 || dyUnconsumed != 0) { int startX = 0; int startY = 0; if (offsetInWindow != null) { this.mView.getLocationInWindow(offsetInWindow); startX = offsetInWindow[0]; startY = offsetInWindow[1]; }ViewParentCompat.onNestedScroll(parent, this.mView, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type); if (offsetInWindow != null) { this.mView.getLocationInWindow(offsetInWindow); offsetInWindow[0] -= startX; offsetInWindow[1] -= startY; }return true; }if (offsetInWindow != null) { offsetInWindow[0] = 0; offsetInWindow[1] = 0; } }return false; }

【3.1.3_CoordinatorLayout原理】以一个自定义的Behavior为例,可以看到调用的对应方法
public class ScaleBehavior extends CoordinatorLayout.Behavior {private FastOutLinearInInterpolator mFastOutLinearInInterpolator = new FastOutLinearInInterpolator(); private LinearOutSlowInInterpolator mLinearOutSlowInInterpolator = new LinearOutSlowInInterpolator(); public ScaleBehavior(Context context, AttributeSet attrs) { super(context, attrs); }@Override public boolean onStartNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) { return axes == ViewCompat.SCROLL_AXIS_VERTICAL; //垂直滚动 }@Override public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) { super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type); if (dyConsumed > 0 && !isRunning && child.getVisibility() == View.VISIBLE){//向下滑动,缩放隐藏控件 scaleHide(child); }else if (dyConsumed < 0 && !isRunning && child.getVisibility() == View.INVISIBLE){ //向上滑动,缩放显示控件 scaleShow(child); } }private boolean isRunning; private void scaleShow(V child) { child.setVisibility(View.VISIBLE); ViewCompat.animate(child) .scaleX(1) .scaleY(1) .setDuration(500) .setInterpolator(mLinearOutSlowInInterpolator) .setListener(new ViewPropertyAnimatorListenerAdapter(){ @Override public void onAnimationStart(View view) { super.onAnimationStart(view); isRunning = true; }@Override public void onAnimationEnd(View view) { super.onAnimationEnd(view); isRunning = false; }@Override public void onAnimationCancel(View view) { super.onAnimationCancel(view); isRunning = false; } }); }private void scaleHide(final V child) { ViewCompat.animate(child) .scaleX(0) .scaleY(0) .setDuration(500) .setInterpolator(mFastOutLinearInInterpolator) .setListener(new ViewPropertyAnimatorListenerAdapter(){ @Override public void onAnimationStart(View view) { super.onAnimationStart(view); isRunning = true; }@Override public void onAnimationEnd(View view) { super.onAnimationEnd(view); isRunning = false; child.setVisibility(View.INVISIBLE); }@Override public void onAnimationCancel(View view) { super.onAnimationCancel(view); isRunning = false; } }); } @Override public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull V child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) { super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, axes, type); } }

    推荐阅读