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

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

http://www.lxweimin.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一系列鏈式調(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.lxweimin.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相當于類View,類B相當于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的條件就是事件的坐標是否在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的條件就是事件的坐標是否在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),準備下一次事件序列流程
            // 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類事件對一些標志位和列表作重置處理,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:如果不攔截,會選擇事件坐標所在的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ù)選擇事件坐標所在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)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,882評論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,208評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 175,746評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,666評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,477評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 54,960評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,047評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,200評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,726評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 40,617評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,807評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,327評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,049評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,425評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,674評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,432評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,769評論 2 372