CoordinateLayout 事件流程分析

以下Coor=CoordinateLayout

首先事件到達CoordinateLayout,回調dispatchTouchEvent,CoordinateLayout並沒有重寫該方法,於是在ViewGroup中:

   @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
.......
        boolean handled = false;

            // Check for interception.
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    intercepted = false;
                }
            } else {
                // There are no touch targets and this action is not an initial down
                // so this view group continues to intercept touches.
                intercepted = true;
            }

OK ,調用了onInterceptTouchEvent。這個方法在Coor類得到了 重寫


 @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        MotionEvent cancelEvent = null;

        final int action = MotionEventCompat.getActionMasked(ev);

        // Make sure we reset in case we had missed a previous important event.
        if (action == MotionEvent.ACTION_DOWN) {
            resetTouchBehaviors();
        }
        //關鍵方法
        final boolean intercepted = performIntercept(ev, TYPE_ON_INTERCEPT);

        if (cancelEvent != null) {
            cancelEvent.recycle();
        }

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            resetTouchBehaviors();
        }

        return intercepted;
    }

關鍵方法:performIntercept(ev, TYPE_ON_INTERCEPT)

    private boolean performIntercept(MotionEvent ev, final int type) {
        boolean intercepted = false;
        boolean newBlock = false;

        MotionEvent cancelEvent = null;

        final int action = MotionEventCompat.getActionMasked(ev);

        final List<View> topmostChildList = mTempList1;
        getTopSortedChildren(topmostChildList);//以繪製的先後順序取出子view(默認情況下)

        // Let topmost child views inspect first
        final int childCount = topmostChildList.size();

        //遍歷所有的子view
        for (int i = 0; i < childCount; i++) {
            final View child = topmostChildList.get(i);
            final LayoutParams lp = (LayoutParams) child.getLayoutParams();
            final Behavior b = lp.getBehavior();//取得子view裏所使用的behavior

            if ((intercepted || newBlock) && action != MotionEvent.ACTION_DOWN) {//不爲ACTION_DOWN 事件,那麼
                // Cancel all behaviors beneath the one that intercepted. 如果有一個behavior對事件進行了攔截,就發送Cancel事件給後續的所有Behavior(其實這和一般的view的Intercept流程差不多)。假設之前還沒有Intercept發生,那麼所有的事件都平等地對所有含有behavior的view進行分發,現在intercept忽然出現,那麼相應的我們就要對除了Intercept的view發出Cancel
                // If the event is "down" then we don't have anything to cancel yet.
                if (b != null) {
                    if (cancelEvent == null) {
                        final long now = SystemClock.uptimeMillis();
                        cancelEvent = MotionEvent.obtain(now, now,
                                MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);構建ACTION_CANCEL事件
                    }
                     //根據傳入的參數不同回調相應事件
                    switch (type) {
                        case TYPE_ON_INTERCEPT:
                            b.onInterceptTouchEvent(this, child, cancelEvent);//注意是cancelEvent!
                            break;
                        case TYPE_ON_TOUCH:
                            b.onTouchEvent(this, child, cancelEvent);
                            break;
                    }
                }
                continue;
            }


            if (!intercepted && b != null) {
                switch (type) {
                    case TYPE_ON_INTERCEPT:
                        intercepted = b.onInterceptTouchEvent(this, child, ev);
                        //注意intercepted變量在這裏有可能被更改!所以是否有intercepted的發生,完全取決於輪詢的behavior的方法onInterceptTouchEvent和下面的onTouchEvent是不是返回true,如果是,事件就會被他截取走,其他的behavior就不會收到進一步的事件,如果返回false,那麼事件還會繼續向其他behavior分發下去
                        break;
                    case TYPE_ON_TOUCH:
                        intercepted = b.onTouchEvent(this, child, ev);
                        break;
                }
                if (intercepted) {
                    mBehaviorTouchView = child;
                }
            }

            // Don't keep going if we're not allowing interaction below this.
            // Setting newBlock will make sure we cancel the rest of the behaviors.
            final boolean wasBlocking = lp.didBlockInteraction();
            final boolean isBlocking = lp.isBlockingInteractionBelow(this, child);
            newBlock = isBlocking && !wasBlocking;
            if (isBlocking && !newBlock) {
                // Stop here since we don't have anything more to cancel - we already did
                // when the behavior first started blocking things below this point.
                break;
            }
        }

        topmostChildList.clear();

        return intercepted;
    }

結束,這個時候就得看看Coor的子view們到底有沒有behavior了,如果有就會被依次回調behavior裏頭的相關方法

然而AppBarLayout的behavior並沒有重寫b.onTouchEvent和b.onInterceptTouchEvent。所以按照默認實現是返回false,沒有進行截取。

好了接下來ViewGroup開始依次調用子view的dispatchTouchEvent,就是正常的事件分發了,後面詳細說這個。
如果最終沒有子view接受事件,那麼就把事件傳回父類(就是viewgroup的父類 view)的dispatchTouchEvent,在那裏會調用onTouchEvent方法,而該方法也在Coor裏頭做了重寫,來看看

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        boolean handled = false;
        boolean cancelSuper = false;
        MotionEvent cancelEvent = null;

        final int action = MotionEventCompat.getActionMasked(ev);

//mBehaviorTouchView記錄的是:如果事件被某個子view的behavior截取的話,那麼這個view就記錄在這個變量裏頭,以後的事件都會傳遞給他。注意條件判斷是||,意味着如果存在這個記錄,那麼performIntercept()就根本不會被執行(因爲已經被截取了嘛)!
下面就會看到這個的用處
        if (mBehaviorTouchView != null || (cancelSuper = performIntercept(ev, TYPE_ON_TOUCH))) {
            // Safe since performIntercept guarantees that
            // mBehaviorTouchView != null if it returns true
            final LayoutParams lp = (LayoutParams) mBehaviorTouchView.getLayoutParams();
            final Behavior b = lp.getBehavior();
            if (b != null) {
                handled = b.onTouchEvent(this, mBehaviorTouchView, ev);
            }
        }

        // Keep the super implementation correct
        if (mBehaviorTouchView == null) {
            handled |= super.onTouchEvent(ev);
        } else if (cancelSuper) {
            if (cancelEvent == null) {
                final long now = SystemClock.uptimeMillis();
                cancelEvent = MotionEvent.obtain(now, now,
                        MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
            }
            super.onTouchEvent(cancelEvent);
        }

        if (!handled && action == MotionEvent.ACTION_DOWN) {

        }

        if (cancelEvent != null) {
            cancelEvent.recycle();
        }

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
            resetTouchBehaviors();
        }

        return handled;
    }

注意第一個if語 句裏頭的performIntercept方法
performIntercept(ev, TYPE_ON_TOUCH),這已經在上面分析過了這個函數,只是這裏傳入的參數是TYPE_ON_TOUCH 而上面傳遞的performIntercept(ev, TYPE_ON_INTERCEPT)。,參數不同進入的就是不同的switch case語句了.

整個流程下來,關鍵的就是兩次的performIntercept調用,第一次是在作爲ViewGroup分發事件之前進行一次performIntercept,第二次是在作爲一個view消費事件之前,也會進行一次

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章