Android的应用层事件分发过程

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://www.jianshu.com/p/fda04a3709ab

https://www.jianshu.com/p/76f94c6452c0文章中最后讲到了ViewRootImpl的setView函数调用,设置View和窗口关联(通过调用mWindowSession的addToDisplay),以及初始化UI draw方面的工作(硬件加速,enableHardwareAcceleration)和初始化接收事件的相关逻辑。本篇主要讲android的应用层事件接收和处理的过程。
本篇的切入点是,android的Native层将消息封装成InputEvent传送到Java层开始(至于如何形成Native层消息,并传送给Java层的,这不是应用开发所关心的,不是本篇的重点)。

事件从ViewRootImpl传递到Activity的根视图DecorView的过程

ViewRootImpl中mWindowSession添加窗口调用addToDisplay的时候,addToDisplay函数有一个参数是InputChannel对象,它代表着ViewRootImpl和WindowManagerService的事件输入关联,WindowManagerService管理的窗口接收到事件之后,会通知到处于焦点下的窗口的ViewRootImpl的InputChannel。
事件最终到InputEventReceiver对象进行接收和处理WindowInputEventReceiver继承InputEventReceiver,InputEventReceiver接收到事件之后调用onInputEvent,由于对象实例是WindowInputEventReceiver对象,所以最终调用了WindowInputEventReceiver的onInputEvent函数,onInputEvent函数里面调用了enqueueInputEvent

//创建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最后无论是调用doProcessInputEvents还是scheduleProcessInputEvents,最终都是调用到doProcessInputEvents函数
doProcessInputEvents为一个while循环,从mPendingInputEventHead表头逐个浏览取出InputEvent,调用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又是如何处理的呢,它比较简单,直接调用InputStage对象stage进行处理,而InputStage是在setView中构造的,如下:

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一系列链式调用之后最终会调用到ViewPostImeInputStage对象的onProcess函数

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);
                }
            }
        }
}

根据事件类型,屏幕触摸事件是SOURCE_CLASS_POINTER类型,调用到processPointerEvent,而processPointerEvent中的eventTarget为Activity的DecorView,事件最终传递到DecorView的dispatchPointerEvent函数

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中调用的dispatchTouchEvent,由于继承关系,所以最终调用到了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中调用了Window的Callback,而Callback在前文中说过(https://www.jianshu.com/p/76f94c6452c0),它是Activity的attach中设置给PhoneWindow的,所以就调用到了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又调用getWindow()的superDispatchTouchEvent(ev)进行事件传递,而getWindow()返回的也是PhoneWindow,所以又将事件传递回了PhoneWindow,来看看Phonewindow的superDispatchTouchEvent调用过程。

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

PhoneWindow的superDispatchTouchEvent又调用了DecorView的superDispatchTouchEvent,而DecorView的superDispatchTouchEvent调用了dispatchTouchEvent,由于DecorView是继承FrameLayout的,也就是继承了ViewGroup,所以兜了一圈,终于又将事件传递到了Activity的根View即DecorView的dispatchTouchEvent。
下面我们来总结下事件传递的第一步,即事件从WindowManagerService传递到Activity的根视图DecorView的过程
1:ViewRootImpl通过mInputEventReceiver对象接收到InputEvent事件输入,mInputEventReceiver调用onInputEvent处理事件。
2:mInputEventReceiver是WindowInputEventReceiver对象,同时继承InputEventReceiver,所以它的onInputEvent事件处理最终调用到ViewRootImpl的enqueueInputEvent,把事件插入到mPendingInputEventTail表尾中,并调用doProcessInputEvents进行处理事件。
3:doProcessInputEvents处理事件最终调用到了DecorView的dispatchPointerEvent,将事件传递到DecorView。
4:DecorView调用PhoneWindow的Callback又将事件传递到Activity的dispatchTouchEvent。
5:Activity的dispatchTouchEvent又返回调用PhoneWindow的superDispatchTouchEvent将事件传递给PhoneWindow,而PhoneWindow最后将事件传递到DecorView即Activity的根视图的dispatchTouchEvent,dispatchTouchEvent之后就是我们熟悉的View树的递归调用,将事件传递给焦点View,这在后面详解。

事件从Activity的根视图DecorView传递给子View的过程

要说清楚这个先得说下递归和View的继承问题。比如阶乘的递归算法

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

比如n=5时。递归调用是factorial(5) = 5factorial(4)=54factorial(3)=543factorial(2)=5432factorial(1)。由于调用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,那么调用b的成员函数dispatchEvent。for循环就会依次递归调用到b1,b2和a的dispatchEvent,即进入到子类的dispatchEvent中,那么依次输出就是b1的dispatchEvent()-->event() 输出:class B event,然后b2也是输出:class B event,最后到a,a的类型为类A,dispatchEvent()中最终调用到自身的event()所以输出class A event。最后b对象调用自身event()输出class B event。
所以输出依次为:class B event class B event class A event class B event。
上面例子中类A相当于类View,类B相当于ViewGroup,如果明白了上述的调用过程,基本上明白了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;

现在直接从ViewGroup的dispatchTouchEvent说起。dispatchTouchEvent代码比较长,截取部分关键代码说明

@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状态和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状态,如果为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类,根据保存的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:根据step4到step6的处理结果。mFirstTouchTarget是否为空,和根据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等事件处理重置状态,准备下一次事件序列流程
            // 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过程中的关键处理过程,step1到step3,可以作为一部分解读。step1中针对DOWN类事件对一些标志位和列表作重置处理,step3作一些是否cancel判断。step2作事件拦截处理。如果需要拦截直接跳转到step7。这个时候mFirstTouchTarget == null成立,调用dispatchTransformedTouchEvent(ev,canceled,null,TouchTarget.ALL_POINTER_IDS);第三个参数为空表明没有child。最终调用父类View的dispatchTouchEvent。最终将事件分发给自身处理。这也就是事件拦截的原理过程。

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        // Perform any necessary transformations and dispatch.
        //child == null为空成立调用父类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中先会调用TouchListener设置的回调,比方说的Button等的回调就是这个时候调用。如果回调返回true表示将事件消耗掉了,就直接返回。如果返回false就调用自身的onTouchEvent处理事件。
如果step2不拦截,则进入step4到step6,选择一个或者多个子View作为事件的分发对象,递归调用dispatchTouchEvent。如果子View在dispatchTouchEvent过程中消耗了事件返回true,则父View也直接返回,不处理事件。如果子View未处理事件。则父View处理事件。
至此我们明白的一个ViewGroup分发事件的过程。1:先查看是否拦截,a:如果拦截,调用自身父类的dispatchTouchEvent分发事件,这之中会调用TouchListener设置的回调或者onTouchEvent处理事件,返回处理result。b:如果不拦截,会选择事件坐标所在的View或者ViewGroup进行事件分发(可能有多个View,依次for循环调用)。然后递归重复上面的过程。
最后来总结下Activity的dispatchTouchEvent函数调用,事件的分发和处理过程
1:Activity的dispatchTouchEvent中调用getWindow()的superDispatchTouchEvent将事件分发给PhoneWindow
2:PhoneWindow中调用成员变量mDecor(DecorView对象)的superDispatchTouchEvent将事件分发给DecorView
3:DecorView作为Activity的根视图同时作为一个ViewGroup。调用dispatchTouchEvent传递事件。
4:DecorView的dispatchTouchEvent分发事件,根据onInterceptTouchEvent觉得是否拦截事件,拦截自己最终调用onTouchEvent处理事件。不拦截根据选择事件坐标所在View范围内的View分发事件,子View根据是否是最终子View递归重复该过程。处理之后依次向上层返回。上层View根据处理result,为true继续上传至根布局。为false调用OnTouchEvent处理事件,然后返回结果。
5:DecorView的dispatchTouchEvent最终处理完成,返回到Activity,Activity根据处理结果为true向上返回,为false调用自身的onTouchEvent处理事件,然后接着向上返回到ViewRootImpl。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,098评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,213评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 149,960评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,519评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,512评论 5 364
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,533评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,914评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,574评论 0 256
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,804评论 1 296
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,563评论 2 319
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,644评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,350评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,933评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,908评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,146评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,847评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,361评论 2 342