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;
}

以一个自定义的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);
    }
}

你可能感兴趣的:(Android深度)