CoordinatorLayout的滑动事件处理机制

来源:互联网 发布:尼尔森数据分析 编辑:程序博客网 时间:2024/06/13 21:41

我们知道,安卓中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就行。

0 0