Android的應(yīng)用層事件分發(fā)過程

版權(quán)聲明:本文為博主原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議丧裁,轉(zhuǎn)載請附上原文出處鏈接和本聲明。
本文鏈接:http://www.reibang.com/p/fda04a3709ab

http://www.reibang.com/p/76f94c6452c0文章中最后講到了ViewRootImpl的setView函數(shù)調(diào)用语稠,設(shè)置View和窗口關(guān)聯(lián)(通過調(diào)用mWindowSession的addToDisplay)脚草,以及初始化UI draw方面的工作(硬件加速,enableHardwareAcceleration)和初始化接收事件的相關(guān)邏輯披坏。本篇主要講android的應(yīng)用層事件接收和處理的過程态坦。
本篇的切入點是,android的Native層將消息封裝成InputEvent傳送到Java層開始(至于如何形成Native層消息棒拂,并傳送給Java層的伞梯,這不是應(yīng)用開發(fā)所關(guān)心的,不是本篇的重點)帚屉。

事件從ViewRootImpl傳遞到Activity的根視圖DecorView的過程

ViewRootImpl中mWindowSession添加窗口調(diào)用addToDisplay的時候谜诫,addToDisplay函數(shù)有一個參數(shù)是InputChannel對象,它代表著ViewRootImpl和WindowManagerService的事件輸入關(guān)聯(lián)攻旦,WindowManagerService管理的窗口接收到事件之后喻旷,會通知到處于焦點下的窗口的ViewRootImpl的InputChannel。
事件最終到InputEventReceiver對象進行接收和處理WindowInputEventReceiver繼承InputEventReceiver牢屋,InputEventReceiver接收到事件之后調(diào)用onInputEvent且预,由于對象實例是WindowInputEventReceiver對象,所以最終調(diào)用了WindowInputEventReceiver的onInputEvent函數(shù)烙无,onInputEvent函數(shù)里面調(diào)用了enqueueInputEvent

//創(chuàng)建mInputEventReceiver對象代碼片段锋谐,具體參考ViewRootImpl的setView
           if (mInputChannel != null) {
                    if (mInputQueueCallback != null) {
                        mInputQueue = new InputQueue();
                        mInputQueueCallback.onInputQueueCreated(mInputQueue);
                    }
                    mInputEventReceiver = new WindowInputEventReceiver(mInputChannel,
                            Looper.myLooper());
                }

final class WindowInputEventReceiver extends InputEventReceiver {
        public WindowInputEventReceiver(InputChannel inputChannel, Looper looper) {
            super(inputChannel, looper);
        }

        @Override
        public void onInputEvent(InputEvent event) {
            enqueueInputEvent(event, this, 0, true);
        }
    }
    WindowInputEventReceiver mInputEventReceiver;

我們來看看enqueueInputEvent的處理過程ViewRootImpl中有兩個成員變量mPendingInputEventHead,mPendingInputEventTail代表輸入事件的隊列的表頭和表尾

QueuedInputEvent mPendingInputEventHead;
QueuedInputEvent mPendingInputEventTail;
void enqueueInputEvent(InputEvent event,
            InputEventReceiver receiver, int flags, boolean processImmediately) {
        QueuedInputEvent q = obtainQueuedInputEvent(event, receiver, flags);
        if (last == null) {
            mPendingInputEventHead = q;
            mPendingInputEventTail = q;
        } else {
            last.mNext = q;
            mPendingInputEventTail = q;
        }
        mPendingInputEventCount += 1;
        
        if (processImmediately) {
            doProcessInputEvents();
        } else {
            scheduleProcessInputEvents();
        }
    }

可以看到將事件event封裝成QueuedInputEvent對象之后截酷,將QueuedInputEvent事件q插入mPendingInputEventTail最后無論是調(diào)用doProcessInputEvents還是scheduleProcessInputEvents涮拗,最終都是調(diào)用到doProcessInputEvents函數(shù)
doProcessInputEvents為一個while循環(huán),從mPendingInputEventHead表頭逐個瀏覽取出InputEvent,調(diào)用deliverInputEvent進行處理合搅。

void doProcessInputEvents() {
        // Deliver all pending input events in the queue.
        while (mPendingInputEventHead != null) {
            QueuedInputEvent q = mPendingInputEventHead;
            mPendingInputEventHead = q.mNext;
            if (mPendingInputEventHead == null) {
                mPendingInputEventTail = null;
            }
            q.mNext = null;

            mPendingInputEventCount -= 1;
            Trace.traceCounter(Trace.TRACE_TAG_INPUT, mPendingInputEventQueueLengthCounterName,
                    mPendingInputEventCount);

            long eventTime = q.mEvent.getEventTimeNano();
            long oldestEventTime = eventTime;
            if (q.mEvent instanceof MotionEvent) {
                MotionEvent me = (MotionEvent)q.mEvent;
                if (me.getHistorySize() > 0) {
                    oldestEventTime = me.getHistoricalEventTimeNano(0);
                }
            }
            mChoreographer.mFrameInfo.updateInputEventTime(eventTime, oldestEventTime);
            deliverInputEvent(q);
        }
    }

那么deliverInputEvent又是如何處理的呢多搀,它比較簡單,直接調(diào)用InputStage對象stage進行處理灾部,而InputStage是在setView中構(gòu)造的康铭,如下:

private void deliverInputEvent(QueuedInputEvent q) {
        InputStage stage;
        if (q.shouldSendToSynthesizer()) {
            stage = mSyntheticInputStage;
        } else {
            stage = q.shouldSkipIme() ? mFirstPostImeInputStage : mFirstInputStage;
        }
        if (stage != null) {
            stage.deliver(q);
        } else {
            finishInputEvent(q);
        }
    }
//代碼片段如下:
// Set up the input pipeline.
                CharSequence counterSuffix = attrs.getTitle();
                mSyntheticInputStage = new SyntheticInputStage();
                InputStage viewPostImeStage = new ViewPostImeInputStage(mSyntheticInputStage);
                InputStage nativePostImeStage = new NativePostImeInputStage(viewPostImeStage,
                        "aq:native-post-ime:" + counterSuffix);
                InputStage earlyPostImeStage = new EarlyPostImeInputStage(nativePostImeStage);
                InputStage imeStage = new ImeInputStage(earlyPostImeStage,
                        "aq:ime:" + counterSuffix);
                InputStage viewPreImeStage = new ViewPreImeInputStage(imeStage);
                InputStage nativePreImeStage = new NativePreImeInputStage(viewPreImeStage,
                        "aq:native-pre-ime:" + counterSuffix);

                mFirstInputStage = nativePreImeStage;
                mFirstPostImeInputStage = earlyPostImeStage;

InputState一系列鏈?zhǔn)秸{(diào)用之后最終會調(diào)用到ViewPostImeInputStage對象的onProcess函數(shù)

final class ViewPostImeInputStage extends InputStage {
        public ViewPostImeInputStage(InputStage next) {
            super(next);
        }

        @Override
        protected int onProcess(QueuedInputEvent q) {
            if (q.mEvent instanceof KeyEvent) {
                return processKeyEvent(q);
            } else {
                final int source = q.mEvent.getSource();
                if ((source & InputDevice.SOURCE_CLASS_POINTER) != 0) {
                    return processPointerEvent(q);
                } else if ((source & InputDevice.SOURCE_CLASS_TRACKBALL) != 0) {
                    return processTrackballEvent(q);
                } else {
                    return processGenericMotionEvent(q);
                }
            }
        }
}

根據(jù)事件類型,屏幕觸摸事件是SOURCE_CLASS_POINTER類型赌髓,調(diào)用到processPointerEvent,而processPointerEvent中的eventTarget為Activity的DecorView从藤,事件最終傳遞到DecorView的dispatchPointerEvent函數(shù)

private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;
            mAttachInfo.mUnbufferedDispatchRequested = false;
            final View eventTarget =
                    (event.isFromSource(InputDevice.SOURCE_MOUSE) && mCapturingView != null) ?
                            mCapturingView : mView;
            mAttachInfo.mHandlingPointerEvent = true;
            boolean handled = eventTarget.dispatchPointerEvent(event);
            maybeUpdatePointerIcon(event);
            mAttachInfo.mHandlingPointerEvent = false;
            if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
                mUnbufferedInputDispatch = true;
                if (mConsumeBatchedInputScheduled) {
                    scheduleConsumeBatchedInputImmediately();
                }
            }
            return handled ? FINISH_HANDLED : FORWARD;
        }

DecorView繼承View,dispatchPointerEvent在View中锁蠕,view中調(diào)用的dispatchTouchEvent夷野,由于繼承關(guān)系,所以最終調(diào)用到了DecorView的dispatchTouchEvent荣倾,下面看看DecorView的dispatchTouchEvent

public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }
//DecorView的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
        final Window.Callback cb = mWindow.getCallback();
        return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
                ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
    }

DecorView中的mWindow是PhoneWindow實例對象悯搔,所以dispatchTouchEvent中調(diào)用了Window的Callback,而Callback在前文中說過(http://www.reibang.com/p/76f94c6452c0)舌仍,它是Activity的attach中設(shè)置給PhoneWindow的妒貌,所以就調(diào)用到了Activity的dispatchTouchEvent通危,來看看Activity的dispatchTouchEvent

//Activity的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

可以看到Activity的dispatchTouchEvent又調(diào)用getWindow()的superDispatchTouchEvent(ev)進行事件傳遞,而getWindow()返回的也是PhoneWindow灌曙,所以又將事件傳遞回了PhoneWindow菊碟,來看看Phonewindow的superDispatchTouchEvent調(diào)用過程。

//PhoneWindow的superDispatchTouchEvent
    public boolean superDispatchTouchEvent(MotionEvent event) {
        return mDecor.superDispatchTouchEvent(event);
    }
//mDecor的superDispatchTouchEvent
public boolean superDispatchTouchEvent(MotionEvent event) {
        return super.dispatchTouchEvent(event);
    }

PhoneWindow的superDispatchTouchEvent又調(diào)用了DecorView的superDispatchTouchEvent在刺,而DecorView的superDispatchTouchEvent調(diào)用了dispatchTouchEvent逆害,由于DecorView是繼承FrameLayout的,也就是繼承了ViewGroup蚣驼,所以兜了一圈魄幕,終于又將事件傳遞到了Activity的根View即DecorView的dispatchTouchEvent。
下面我們來總結(jié)下事件傳遞的第一步隙姿,即事件從WindowManagerService傳遞到Activity的根視圖DecorView的過程
1:ViewRootImpl通過mInputEventReceiver對象接收到InputEvent事件輸入梅垄,mInputEventReceiver調(diào)用onInputEvent處理事件。
2:mInputEventReceiver是WindowInputEventReceiver對象输玷,同時繼承InputEventReceiver队丝,所以它的onInputEvent事件處理最終調(diào)用到ViewRootImpl的enqueueInputEvent,把事件插入到mPendingInputEventTail表尾中欲鹏,并調(diào)用doProcessInputEvents進行處理事件机久。
3:doProcessInputEvents處理事件最終調(diào)用到了DecorView的dispatchPointerEvent,將事件傳遞到DecorView赔嚎。
4:DecorView調(diào)用PhoneWindow的Callback又將事件傳遞到Activity的dispatchTouchEvent膘盖。
5:Activity的dispatchTouchEvent又返回調(diào)用PhoneWindow的superDispatchTouchEvent將事件傳遞給PhoneWindow,而PhoneWindow最后將事件傳遞到DecorView即Activity的根視圖的dispatchTouchEvent尤误,dispatchTouchEvent之后就是我們熟悉的View樹的遞歸調(diào)用侠畔,將事件傳遞給焦點View,這在后面詳解损晤。

事件從Activity的根視圖DecorView傳遞給子View的過程

要說清楚這個先得說下遞歸和View的繼承問題软棺。比如階乘的遞歸算法

int factorial(int n){
      if(n<=1) return 1;
      else return factorial(n-1);
}

比如n=5時。遞歸調(diào)用是factorial(5) = 5factorial(4)=54factorial(3)=543factorial(2)=5432factorial(1)尤勋。由于調(diào)用factorial(1)時滿足if(n<=1) return 1喘落。所有factorial(5)=54321。
在來舉一個繼承的例子最冰,有類A和類B瘦棋,類B繼承類A

class A {
          public void event(){
                log.d("class A event");
          }
        public void dispatchEvent(){
                event();
          }
}

class B extends A{
          private A[] mChildren;
          public void event() {
                log.d("class B event");
          }
          public void dispatchEvent() {
                  if(mChildren != null) {
                        for(int i = 0; i < mChildren.length();++i){
                              mChildren[i].dispatchEvent();
                    }
                 event();
              }        
          }
}

比如上面類B的實例b,它的mChildren有3個實例對象暖哨,分別是b1赌朋,b2和a,那么調(diào)用b的成員函數(shù)dispatchEvent。for循環(huán)就會依次遞歸調(diào)用到b1箕慧,b2和a的dispatchEvent服球,即進入到子類的dispatchEvent中,那么依次輸出就是b1的dispatchEvent()-->event() 輸出:class B event颠焦,然后b2也是輸出:class B event,最后到a往枣,a的類型為類A伐庭,dispatchEvent()中最終調(diào)用到自身的event()所以輸出class A event。最后b對象調(diào)用自身event()輸出class B event分冈。
所以輸出依次為:class B event class B event class A event class B event圾另。
上面例子中類A相當(dāng)于類View,類B相當(dāng)于ViewGroup雕沉,如果明白了上述的調(diào)用過程集乔,基本上明白了View的事件的傳遞的百分六七十。實際的ViewGroup中可能還有onInterceptTouchEvent事件攔截坡椒,以及事件有ACTION_DOWN和ACTION_UP扰路,ACTION_MOVE等不同類型引入了mFirstTouchTarget這個鏈表對象處理差異。

public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.isFromSource(InputDevice.SOURCE_MOUSE)
                && ev.getAction() == MotionEvent.ACTION_DOWN
                && ev.isButtonPressed(MotionEvent.BUTTON_PRIMARY)
                && isOnScrollbarThumb(ev.getX(), ev.getY())) {
            return true;
        }
        return false;
    }
// First touch target in the linked list of touch targets.
    private TouchTarget mFirstTouchTarget;

現(xiàn)在直接從ViewGroup的dispatchTouchEvent說起倔叼。dispatchTouchEvent代碼比較長汗唱,截取部分關(guān)鍵代碼說明

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        boolean handled = false;
        //安全檢查,認為返回true丈攒,忽略
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            //step1:如果是ACTION_DOWN事件哩罪,表明是一個事件序列的開始,reset狀態(tài)和clear mFirstTouchTarget
            // Handle an initial down.
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            //step2:檢查是否進行事件攔截
            // 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 {
                intercepted = true;
            }
            //step3:檢查是否是cancel狀態(tài)巡验,如果為True际插,則不進入
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            // Update list of touch targets for pointer down, if needed.
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            if (!canceled && !intercepted) {
                View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                        ? findChildWithAccessibilityFocus() : null;
                //step4:檢查是否DOWN類事件,是才進入显设,不是則表示UP或者MOVE類框弛,根據(jù)保存的mFirstTouchTarget進
                //行事件傳遞
                if (actionMasked == MotionEvent.ACTION_DOWN
                        || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                        || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                    final int actionIndex = ev.getActionIndex(); // always 0 for down
                    final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                            : TouchTarget.ALL_POINTER_IDS;

                    // Clean up earlier touch targets for this pointer id in case they
                    // have become out of sync.
                    removePointersFromTouchTargets(idBitsToAssign);

                //step5:注釋寫的很清楚Find a child that can receive the event.
                //find的條件就是事件的坐標(biāo)是否在View上和View是否能接收事件
                    final int childrenCount = mChildrenCount;
                    if (newTouchTarget == null && childrenCount != 0) {
                        final float x = ev.getX(actionIndex);
                        final float y = ev.getY(actionIndex);
                        // Find a child that can receive the event.
                        // Scan children from front to back.
                        final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = getAndVerifyPreorderedIndex(
                                    childrenCount, i, customOrder);
                            final View child = getAndVerifyPreorderedView(
                                    preorderedList, children, childIndex);

                            // If there is a view that has accessibility focus we want it
                            // to get the event first and if not handled we will perform a
                            // normal dispatch. We may do a double iteration but this is
                            // safer given the timeframe.
                            if (childWithAccessibilityFocus != null) {
                                if (childWithAccessibilityFocus != child) {
                                    continue;
                                }
                                childWithAccessibilityFocus = null;
                                i = childrenCount - 1;
                            }
                            //find的條件就是事件的坐標(biāo)是否在View上和View是否能接收事件,這樣就避免了逐個dispathch的問題
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }

                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                // Child is already receiving touch within its bounds.
                                // Give it the new pointer in addition to the ones it is handling.
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }

                            resetCancelNextUpFlag(child);
                            //step6:傳遞事件到children View敷硅,成功則將View保存在mFirstTouchTarget列表中
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // Child wants to receive touch within its bounds.
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex points into presorted list, find original index
                                    for (int j = 0; j < childrenCount; j++) {
                                        if (children[childIndex] == mChildren[j]) {
                                            mLastTouchDownIndex = j;
                                            break;
                                        }
                                    }
                                } else {
                                    mLastTouchDownIndex = childIndex;
                                }
                                mLastTouchDownX = ev.getX();
                                mLastTouchDownY = ev.getY();
                                newTouchTarget = addTouchTarget(child, idBitsToAssign);
                                alreadyDispatchedToNewTouchTarget = true;
                                break;
                            }

                            // The accessibility focus didn't handle the event, so clear
                            // the flag and do a normal dispatch to all children.
                            ev.setTargetAccessibilityFocus(false);
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }
                }
            }

            //step7:根據(jù)step4到step6的處理結(jié)果功咒。mFirstTouchTarget是否為空,和根據(jù)UP绞蹦,MOVE等事件進行不同的處理
            // Dispatch to touch targets.
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        if (dispatchTransformedTouchEvent(ev, cancelChild,
                                target.child, target.pointerIdBits)) {
                            handled = true;
                        }
                        if (cancelChild) {
                            if (predecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                        }
                    }
                    predecessor = target;
                    target = next;
                }
            }

            //step8:UP等事件處理重置狀態(tài)力奋,準(zhǔn)備下一次事件序列流程
            // Update list of touch targets for pointer up or cancel, if needed.
            if (canceled
                    || actionMasked == MotionEvent.ACTION_UP
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                final int actionIndex = ev.getActionIndex();
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                removePointersFromTouchTargets(idBitsToRemove);
            }
        }

        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;
    }

上面的注釋step1到step8主要是事件的dispatch過程中的關(guān)鍵處理過程,step1到step3幽七,可以作為一部分解讀景殷。step1中針對DOWN類事件對一些標(biāo)志位和列表作重置處理,step3作一些是否cancel判斷。step2作事件攔截處理猿挚。如果需要攔截直接跳轉(zhuǎn)到step7咐旧。這個時候mFirstTouchTarget == null成立,調(diào)用dispatchTransformedTouchEvent(ev,canceled,null,TouchTarget.ALL_POINTER_IDS);第三個參數(shù)為空表明沒有child。最終調(diào)用父類View的dispatchTouchEvent航揉。最終將事件分發(fā)給自身處理爬凑。這也就是事件攔截的原理過程。

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        // Perform any necessary transformations and dispatch.
        //child == null為空成立調(diào)用父類View的dispatchTouchEvent
        if (child == null) {
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {
                transformedEvent.transform(child.getInverseMatrix());
            }

            handled = child.dispatchTouchEvent(transformedEvent);
        }

        // Done.
        transformedEvent.recycle();
        return handled;
    }

View的dispatchTouchEvent
public boolean dispatchTouchEvent(MotionEvent event) {
        boolean result = false;
        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        return result;
    }

可以看到View的dispatchTouchEvent中先會調(diào)用TouchListener設(shè)置的回調(diào)伊约,比方說的Button等的回調(diào)就是這個時候調(diào)用。如果回調(diào)返回true表示將事件消耗掉了孕蝉,就直接返回屡律。如果返回false就調(diào)用自身的onTouchEvent處理事件。
如果step2不攔截降淮,則進入step4到step6超埋,選擇一個或者多個子View作為事件的分發(fā)對象,遞歸調(diào)用dispatchTouchEvent佳鳖。如果子View在dispatchTouchEvent過程中消耗了事件返回true霍殴,則父View也直接返回,不處理事件腋颠。如果子View未處理事件繁成。則父View處理事件。
至此我們明白的一個ViewGroup分發(fā)事件的過程淑玫。1:先查看是否攔截巾腕,a:如果攔截,調(diào)用自身父類的dispatchTouchEvent分發(fā)事件絮蒿,這之中會調(diào)用TouchListener設(shè)置的回調(diào)或者onTouchEvent處理事件尊搬,返回處理result。b:如果不攔截土涝,會選擇事件坐標(biāo)所在的View或者ViewGroup進行事件分發(fā)(可能有多個View佛寿,依次for循環(huán)調(diào)用)。然后遞歸重復(fù)上面的過程但壮。
最后來總結(jié)下Activity的dispatchTouchEvent函數(shù)調(diào)用冀泻,事件的分發(fā)和處理過程
1:Activity的dispatchTouchEvent中調(diào)用getWindow()的superDispatchTouchEvent將事件分發(fā)給PhoneWindow
2:PhoneWindow中調(diào)用成員變量mDecor(DecorView對象)的superDispatchTouchEvent將事件分發(fā)給DecorView
3:DecorView作為Activity的根視圖同時作為一個ViewGroup。調(diào)用dispatchTouchEvent傳遞事件蜡饵。
4:DecorView的dispatchTouchEvent分發(fā)事件弹渔,根據(jù)onInterceptTouchEvent覺得是否攔截事件,攔截自己最終調(diào)用onTouchEvent處理事件溯祸。不攔截根據(jù)選擇事件坐標(biāo)所在View范圍內(nèi)的View分發(fā)事件肢专,子View根據(jù)是否是最終子View遞歸重復(fù)該過程舞肆。處理之后依次向上層返回。上層View根據(jù)處理result博杖,為true繼續(xù)上傳至根布局椿胯。為false調(diào)用OnTouchEvent處理事件,然后返回結(jié)果剃根。
5:DecorView的dispatchTouchEvent最終處理完成哩盲,返回到Activity,Activity根據(jù)處理結(jié)果為true向上返回狈醉,為false調(diào)用自身的onTouchEvent處理事件种冬,然后接著向上返回到ViewRootImpl。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末舔糖,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子莺匠,更是在濱河造成了極大的恐慌金吗,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,640評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趣竣,死亡現(xiàn)場離奇詭異摇庙,居然都是意外死亡,警方通過查閱死者的電腦和手機遥缕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評論 3 395
  • 文/潘曉璐 我一進店門卫袒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人单匣,你說我怎么就攤上這事夕凝。” “怎么了户秤?”我有些...
    開封第一講書人閱讀 165,011評論 0 355
  • 文/不壞的土叔 我叫張陵码秉,是天一觀的道長。 經(jīng)常有香客問我鸡号,道長转砖,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,755評論 1 294
  • 正文 為了忘掉前任鲸伴,我火速辦了婚禮府蔗,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘汞窗。我一直安慰自己姓赤,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,774評論 6 392
  • 文/花漫 我一把揭開白布杉辙。 她就那樣靜靜地躺著模捂,像睡著了一般捶朵。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上狂男,一...
    開封第一講書人閱讀 51,610評論 1 305
  • 那天综看,我揣著相機與錄音,去河邊找鬼岖食。 笑死红碑,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的泡垃。 我是一名探鬼主播析珊,決...
    沈念sama閱讀 40,352評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼蔑穴!你這毒婦竟也來了忠寻?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,257評論 0 276
  • 序言:老撾萬榮一對情侶失蹤存和,失蹤者是張志新(化名)和其女友劉穎奕剃,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體捐腿,經(jīng)...
    沈念sama閱讀 45,717評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡纵朋,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,894評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了茄袖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片操软。...
    茶點故事閱讀 40,021評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖宪祥,靈堂內(nèi)的尸體忽然破棺而出聂薪,到底是詐尸還是另有隱情,我是刑警寧澤品山,帶...
    沈念sama閱讀 35,735評論 5 346
  • 正文 年R本政府宣布胆建,位于F島的核電站,受9級特大地震影響肘交,放射性物質(zhì)發(fā)生泄漏笆载。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,354評論 3 330
  • 文/蒙蒙 一涯呻、第九天 我趴在偏房一處隱蔽的房頂上張望凉驻。 院中可真熱鬧,春花似錦复罐、人聲如沸涝登。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽胀滚。三九已至趟济,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間咽笼,已是汗流浹背顷编。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剑刑,地道東北人媳纬。 一個月前我還...
    沈念sama閱讀 48,224評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像施掏,于是被迫代替她去往敵國和親钮惠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,974評論 2 355