CoordinatorLayout的滑动事件处理机制

我们知道,安卓中View事件处理是Activity—>Window—>
DecorView—>ViewGroup—>View。当然也可以中途拦截。那么当我们在FloatingActionButton的布局文件中设置layout_anchor、layout_anchorGravity和layout_behavior时,RecyclerView的滑动事件是如何传递的呢?

以onNestedFling方法为例

case MotionEvent.ACTION_UP: {
                mVelocityTracker.addMovement(vtev);
                eventAddedToVelocityTracker = true;
                mVelocityTracker.computeCurrentVelocity(1000, mMaxFlingVelocity);
                final float xvel = canScrollHorizontally ?
                        -VelocityTrackerCompat.getXVelocity(mVelocityTracker, mScrollPointerId) : 0;
                final float yvel = canScrollVertically ?
                        -VelocityTrackerCompat.getYVelocity(mVelocityTracker, mScrollPointerId) : 0;
                if (!((xvel != 0 || yvel != 0) && fling((int) xvel, (int) yvel))) {
                    setScrollState(SCROLL_STATE_IDLE);
                }

在此过程中会调用fling方法,此方法会先调用onNestedPreFling来处理,如果返回false就接着调用onNestedFling。当然不是直接调用,而是先通过调用NestedScrollingChildHelper类中相应的方法,NestedScrollingChildHelper是ViewParentCompat的代理类,ViewParentCompat会调用FloatingActionButton的父控件来最终调用我们自己Behavior类实现的方法。如下所示:

//RecyclerView中的方法
    @Override
    public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        return mScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed);//mScrollingChildHelper是NestedScrollingChildHelper的实例
    }
//NestedScrollingChildHelper类中的方法
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
        if (isNestedScrollingEnabled() 
        && mNestedScrollingParent != null) {
            return ViewParentCompat.onNestedFling(mNestedScrollingParent, mView, velocityX,velocityY, consumed);
        }
        return false;
    }//mNestedScrollingParent为mView的父控件
//ViewParentCompat中的方法
public static boolean onNestedFling(ViewParent parent, View target, float velocityX,
            float velocityY, boolean consumed) {
        return IMPL.onNestedFling(parent, target, velocityX, velocityY, consumed);
    }
//ViewParentCompat中onNestedFling方法的实现
@Override
        public boolean onNestedFling(ViewParent parent, View target, float velocityX,
                float velocityY, boolean consumed) {
            if (parent instanceof NestedScrollingParent) {
                return ((NestedScrollingParent) parent).onNestedFling(target, velocityX, velocityY,
                        consumed);
            }
            return false;
        }

从这里可以看出相应的事件已经传到父控件中了,即CoordinatorLayout类中,相关实现如下:

//CoordinatorLayout中的方法
public boolean onNestedFling(View target, float velocityX, float velocityY, boolean consumed) {
        boolean handled = false;

        final int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View view = getChildAt(i);
            final LayoutParams lp = (LayoutParams) view.getLayoutParams();
            if (!lp.isNestedScrollAccepted()) {
                continue;
            }

            final Behavior viewBehavior = lp.getBehavior();
            if (viewBehavior != null) {
                handled |= viewBehavior.onNestedFling(this, view, target, velocityX, velocityY,
                        consumed);
            }
        }
        if (handled) {
            dispatchOnDependentViewChanged(true);
        }
        return handled;
    }

以前的观念中,当子控件不能处理相关事件时会返回false,交由上级处理,而FloatingActionButton则不是,它是调用父控件中相应的方法,然后父控件遍历子类并最终调用我们的实现。我们反过来想也会知道,RecyclerView是没法调用我们的Behavior实现的,通过父控件来调用才是高明之法。

同时我们也应该知道,FloatingActionButton(archor的View)的父控件必须为CoordinatorLayout,不能中间套上LinearLayout或者其他ViewGroup类的实现,因为他们并没有相应的实现,不然会发生Could not find CoordinatorLayout descendant view with id ……的错误。对于被archor的View则没有要求,从NestedScrollingChildHelper对于父控件的赋值可以看出:

//NestedScrollingChildHelper的startNestedScroll方法部分代码
if (isNestedScrollingEnabled()) {
            ViewParent p = mView.getParent();
            View child = mView;
            while (p != null) {
                if (ViewParentCompat.onStartNestedScroll(p, child, mView, axes)) {
                    mNestedScrollingParent = p;
                    ViewParentCompat.onNestedScrollAccepted(p, child, mView, axes);
                    return true;
                }
                if (p instanceof View) {
                    child = (View) p;
                }
                p = p.getParent();
            }
        }

ViewParentCompat.onStartNestedScroll(p, child, mView, axes)会判断p是否是NestedScrollingParent的实例。代码如下:

//ViewParentCompat方类中的方法
@Override
        public boolean onStartNestedScroll(ViewParent parent, View child, View target,
                int nestedScrollAxes) {
            if (parent instanceof NestedScrollingParent) {
                return ((NestedScrollingParent) parent).onStartNestedScroll(child, target,
                        nestedScrollAxes);
            }
            return false;
        }

所以当父控件为LinearLayout或者RelativeLayout或其他非CoordinatorLayout时会返回false,接着访问其父控件,直到碰到CoordinatorLayout,否则返回false。所以对于被archor的view具体父控件没有什么要求,只要最终是CoordinatorLayout就行。

你可能感兴趣的:(android)