深入理解Android事件分发机制

一、Android事件的入口

(该文所引用的源码代码出自Android 5.1版本)
一直想搞清楚Android 事件分发的各个函数调用的关系,前几天突然想起Debug时会显示出线程函数调用的关系,于是,写了一个很简单的程序,在界面上显示一个Button,并且为这个Button注册onTouch事件,在里面打个断点:

button.setOnTouchListener(new View.OnTouchListener() {

            @Override
            public boolean onTouch(View v, MotionEvent event) {
                return false;
            }
        });

然后一运行程序,点击一下按钮,果然,函数之间的调用关系出来了:

深入理解Android事件分发机制_第1张图片

从上面的图可以看到事件分发时各个函数调用的先后顺序,为了看的更加清楚,画了一个简单的流程图,蓝色背景的表示我们可以通过重写父类的方法控制和改变的方法:

当点击事件时,native层调用Java层InputEventReceiver中的dispatchInputEvent方法,名字很好理解,是用来分发输入事件的:

 // Called from native code.
    @SuppressWarnings("unused")
    private void dispatchInputEvent(int seq, InputEvent event) {
        mSeqMap.put(event.getSequenceNumber(), seq);
        onInputEvent(event);
    }

这个方法接着调用WindowInputEventReceiver的onInputEvent方法,接着还有一系列的调用,最后,会调用ViewRootImpl中的processPointerEvent方法:

 private int processPointerEvent(QueuedInputEvent q) {
            final MotionEvent event = (MotionEvent)q.mEvent;

            mAttachInfo.mUnbufferedDispatchRequested = false;
            boolean handled = mView.dispatchPointerEvent(event);
            if (mAttachInfo.mUnbufferedDispatchRequested && !mUnbufferedInputDispatch) {
                mUnbufferedInputDispatch = true;
                if (mConsumeBatchedInputScheduled) {
                    scheduleConsumeBatchedInputImmediately();
                }
            }
            return handled ? FINISH_HANDLED : FORWARD;
        }

processPointerEvent方法第4行,调用了mView.dispatchPointerEvent(event),找到最开始调用View方法的地方,那这个mView究竟是何方神圣?通过一系列代码追踪,mView就是每个Activity实例里面的mDecor,也就是DecorView的一个实例。通过查看布局层次,可以发现DecorView是最顶层的View,所以可以确定,Android事件的分发是从DecorView开始的。

深入理解Android事件分发机制_第2张图片

mView.dispatchPointerEvent(event)相当于调用View的dispatchPointerEvent方法:

 public final boolean dispatchPointerEvent(MotionEvent event) {
        if (event.isTouchEvent()) {
            return dispatchTouchEvent(event);
        } else {
            return dispatchGenericMotionEvent(event);
        }
    }

在方法里面会调用dispatchTouchEvent(event),因为DecorView重写了dispatchTouchEvent(event)方法,所以会调用DecorView的dispatchTouchEvent(event):

  @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {
            final Callback cb = getCallback();
            return cb != null && !isDestroyed() && mFeatureId < 0 ? cb.dispatchTouchEvent(ev)
                    : super.dispatchTouchEvent(ev);
        }

从方法里面可以看到,如果cb不为空,并且窗口没有销毁时调用cb.dispatchTouchEvent(ev),而Callback是Window类的接口:

/** * API from a Window back to its caller. This allows the client to * intercept key dispatching, panels and menus, etc. */
    public interface Callback {

        public boolean dispatchKeyEvent(KeyEvent event);

        public boolean dispatchKeyShortcutEvent(KeyEvent event);

        public boolean dispatchTouchEvent(MotionEvent event);

        public boolean dispatchTrackballEvent(MotionEvent event);
        ......
    }

而Activity实现了这个接口:
深入理解Android事件分发机制_第3张图片

所以,会调用Activity中的dispatchTouchEvent方法:

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

在里面调用DecorView的superDispatchTouchEvent方法:

     public boolean superDispatchTouchEvent(MotionEvent event) {
            return super.dispatchTouchEvent(event);
        }

接着调用super.dispatchTouchEvent(event),也就是调用ViewGroup的dispatchTouchEvent(event),开始一级一级向下传递。

二、ViewGroup的事件分发

ViewGroup事件分发主要通过dispatchTouchEvent方法完成:

public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
        }

        // If the event targets the accessibility focused view and this is it,
        // start
        // normal event dispatch. Maybe a descendant is what will handle the
        // click.
        if (ev.isTargetAccessibilityFocus()
                && isAccessibilityFocusedViewOrHost()) {
            ev.setTargetAccessibilityFocus(false);
        }

        boolean handled = false;
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Handle an initial down.
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                // Throw away all previous state when starting a new touch
                // gesture.
                // The framework may have dropped the up or cancel event for the
                // previous gesture
                // due to an app switch, ANR, or some other state change.
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }

            // Check for interception.
            final boolean intercepted;
            // 动作为手指按下的时候
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                // requestDisallowInterceptTouchEvent调用,不让父View拦截事件
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {
                    // 调用onInterceptTouchEvent,判断是否拦截事件
                    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;
            }
            // If intercepted, start normal event dispatch. Also if there is
            // already
            // a view that is handling the gesture, do normal event dispatch.
            if (intercepted || mFirstTouchTarget != null) {
                ev.setTargetAccessibilityFocus(false);
            }

            // Check for cancelation.
            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) {

                // If the event is targeting accessiiblity focus we give it to
                // the
                // view that has accessibility focus and if it does not handle
                // it
                // we clear the flag and dispatch the event to all children as
                // usual.
                // We are looking up the accessibility focused host to avoid
                // keeping
                // state since these events are very rare.
                View childWithAccessibilityFocus = ev
                        .isTargetAccessibilityFocus() ? findChildWithAccessibilityFocus()
                        : null;
                // 判断触摸事件的种类
                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);

                    final int childrenCount = mChildrenCount;
                    // 如果有子View
                    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.
                        // 所有的子View从上往下排序,或是是绘制的顺序
                        final ArrayList<View> preorderedList = buildOrderedChildList();
                        final boolean customOrder = preorderedList == null
                                && isChildrenDrawingOrderEnabled();
                        final View[] children = mChildren;
                        // 遍历所有的子View
                        for (int i = childrenCount - 1; i >= 0; i--) {
                            final int childIndex = customOrder ? getChildDrawingOrder(
                                    childrenCount, i) : i;
                            // 获取子View
                            final View child = (preorderedList == null) ? children[childIndex]
                                    : preorderedList.get(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;
                            }
                            // 判断子View是否VISIBLE,点击坐标是否在View的范围内
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y,
                                            child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                            // 得到TouchTarget
                            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);
                            //如果触摸位置在child的范围内,进行事件分发
                            if (dispatchTransformedTouchEvent(ev, false, child,
                                    idBitsToAssign)) {
                                // Child wants to receive touch within its
                                // bounds.
                                // 如果子View消费了Touch事件
                                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();
                    }

                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                        // Did not find a child to receive the event.
                        // Assign the pointer to the least recently added
                        // target.
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }

            // Dispatch to touch targets.
            // 只有调用addTouchTarget,mFirstTouchTarget才可能不为空。要调用addTouchTarget
            // 必须有子View消费了Action.Down事件,否则super.dispatchTouchEvent(event),ViewGroup把自己当做View
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                //View消费了Action.Down事件,MOVE,UP等事件开始执行
                // 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;
                }
            }

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

代码比较长,只分析比较关键的代码34行有一个判断:

            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null)

只有动作为ACTION_DOWN或者mFirstTouchTarget != null时才会往下走。
第37行:

final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;

这里会得到一个boolean变量,这个变量主要用处是控制ViewGroup在分发事件时是否调用onInterceptTouchEvent方法:

                if (!disallowIntercept) {
                    // 调用onInterceptTouchEvent,判断是否拦截事件
                    intercepted = onInterceptTouchEvent(ev);
                    ev.setAction(action); // restore action in case it was
                                          // changed
                }

只有当disallowIntercept的值为false时,才会调用onInterceptTouchEvent方法,而disallowIntercept 的取值取决于我们调用requestDisallowInterceptTouchEvent这个方法:

public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
            // We're already in this state, assume our ancestors are too
            return;
        }

        if (disallowIntercept) {
            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
        } else {
            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
        }

        // Pass it up to our parent
        if (mParent != null) {
            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
    }

当传入参数为true时,disallowIntercept的值为true,不会在调用onInterceptTouchEvent方法,这个方法常常是子View调用,不让父View拦截事件。
40行intercepted变量控制父View是否拦截事件,它的取值除了受到上面disallowIntercept的控制外,由onInterceptTouchEvent方法的返回值控制:

 public boolean onInterceptTouchEvent(MotionEvent ev) {
        return false;
    }

默认是返回false,是不拦截子View的事件。
99行,判断该ViewGroup是否包含子View,如果包含子View,遍历子View,132行调用isTransformedTouchPointInView判断触摸的位置是否在View的范围内:

 protected boolean isTransformedTouchPointInView(float x, float y,
            View child, PointF outLocalPoint) {
        final float[] point = getTempPoint();
        point[0] = x;
        point[1] = y;
        transformPointToViewLocal(point, child);
        // 判断点击的坐标是否在View的范围内
        final boolean isInView = child.pointInView(point[0], point[1]);
        if (isInView && outLocalPoint != null) {
            outLocalPoint.set(point[0], point[1]);
        }

        return isInView;
    }

如果触摸在子View的范围类,则通过调用dispatchTransformedTouchEvent进行子View的事件分发:

private boolean dispatchTransformedTouchEvent(MotionEvent event,
            boolean cancel, View child, int desiredPointerIdBits) {
        final boolean handled;

        // Canceling motions is a special case. We don't need to perform any
        // transformations
        // or filtering. The important part is the action, not the contents.
        final int oldAction = event.getAction();
        if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
            event.setAction(MotionEvent.ACTION_CANCEL);
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                handled = child.dispatchTouchEvent(event);
            }
            event.setAction(oldAction);
            return handled;
        }

        // Calculate the number of pointers to deliver.
        final int oldPointerIdBits = event.getPointerIdBits();
        final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;

        // If for some reason we ended up in an inconsistent state where it
        // looks like we
        // might produce a motion event with no pointers in it, then drop the
        // event.
        // 不是手指触发的点击事件,直接返回
        if (newPointerIdBits == 0) {
            return false;
        }

        // If the number of pointers is the same and we don't need to perform
        // any fancy
        // irreversible transformations, then we can reuse the motion event for
        // this
        // dispatch as long as we are careful to revert any changes we make.
        // Otherwise we need to make a copy.
        final MotionEvent transformedEvent;
        if (newPointerIdBits == oldPointerIdBits) {
            if (child == null || child.hasIdentityMatrix()) {
                if (child == null) {
                    // 子View为空,交给ViewGroup处理
                    handled = super.dispatchTouchEvent(event);
                } else {
                    final float offsetX = mScrollX - child.mLeft;
                    final float offsetY = mScrollY - child.mTop;
                    event.offsetLocation(offsetX, offsetY);
                    // 调用子View的事件分发
                    handled = child.dispatchTouchEvent(event);

                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            // 从池子里面获取一个MotionEvent
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }

        // Perform any necessary transformations and dispatch.
        if (child == null) {
            // 子View为空,ViewGroup调用View的dispatchTouchEvent,接着调用自己的onTouchEvent
            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());
            }
            // 调用子View的事件分发
            handled = child.dispatchTouchEvent(transformedEvent);
        }

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

在这个方法里面,又会调用子View的dispatchTouchEvent方法,一直递归下去,这时会分两种情况:
第一种:如果外层的View传递的过程中,有View消费了事件,则传递停止,继续往下执行。
第二种:已经传递到最外层的View,但是最外层的View仍然不消费事件,则沿着事件传递的反方向继续传递,交给该View的父View消费事件,继续递归往里面传递,直到消费事件。
203行有个判断:

if (mFirstTouchTarget == null)

只有调用addTouchTarget,mFirstTouchTarget才可能不为空。要调用addTouchTarget 必须有子View消费了Action.Down事件,否则调用super.dispatchTouchEvent(event),也就是View的分发方法。
如果有子View消费了Action.Down事件,那么会走到else里面,一般只有MOVE,UP动作会走到这里,222行又是调用dispatchTransformedTouchEvent进行事件分发。

三、View的事件分发

ViewGroup进行事件分发时,一般最好分发到View,调用View的dispatchTouchEvent,View的dispatchTouchEvent方法相对比较简单:

 public boolean dispatchTouchEvent(MotionEvent event) {
        // If the event should be handled by accessibility focus first.
        if (event.isTargetAccessibilityFocus()) {
            // We don't have focus or no virtual descendant has it, do not handle the event.
            if (!isAccessibilityFocusedViewOrHost()) {
                return false;
            }
            // We have focus and got the event, then use normal event dispatch.
            event.setTargetAccessibilityFocus(false);
        }

        boolean result = false;

        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(event, 0);
        }

        final int actionMasked = event.getActionMasked();
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // Defensive cleanup for new gesture
            stopNestedScroll();
        }

        if (onFilterTouchEventForSecurity(event)) {
            //noinspection SimplifiableIfStatement
            ListenerInfo li = mListenerInfo;
            //1、判断是否设置OnTouchListener
            //2、判断View的状态是否是ENABLED
            //3、判断View onTouch 是返回true,是否消费事件
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }
            //如果View的 onTouch没有消费事件,执行onTouchEvent
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }

        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }

        // Clean up after nested scrolls if this is the end of a gesture;
        // also cancel it if we tried an ACTION_DOWN but we didn't want the rest
        // of the gesture.
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            stopNestedScroll();
        }

        return result;
    }

30行的判断,满足三个条件,将不会调用View的onTouchEvent,如果在Activity里面,我们为某个状态为ENABLED的View设置了mOnTouchListener事件,并且onTouch方法返回true,那么相当于消费了事件,36行onTouchEvent不会执行,本次View的事件分发就此结束。
如果这三个条件有一个不满足,那么接着会调用onTouchEvent方法,进行事件的处理:

 public boolean onTouchEvent(MotionEvent event) {
        final float x = event.getX();
        final float y = event.getY();
        final int viewFlags = mViewFlags;

        //View 的状态是DISABLED,如果是可点击或者可以长按,可以消费事件
        if ((viewFlags & ENABLED_MASK) == DISABLED) {
            if (event.getAction() == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
                setPressed(false);
            }
            // A disabled view that is clickable still consumes the touch
            // events, it just doesn't respond to them.
            return (((viewFlags & CLICKABLE) == CLICKABLE ||
                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
        }

        if (mTouchDelegate != null) {
            if (mTouchDelegate.onTouchEvent(event)) {
                return true;
            }
        }
        //如果View是可以点击的
        if (((viewFlags & CLICKABLE) == CLICKABLE ||
                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_UP:
                    boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                        // take focus if we don't have it already and we should in
                        // touch mode.
                        boolean focusTaken = false;
                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                            focusTaken = requestFocus();
                        }

                        if (prepressed) {
                            // The button is being released before we actually
                            // showed it as pressed. Make it show the pressed
                            // state now (before scheduling the click) to ensure
                            // the user sees it.
                            setPressed(true, x, y);
                       }

                        if (!mHasPerformedLongPress) {
                            // This is a tap, so remove the longpress check
                            //移除长按的回调函数
                            removeLongPressCallback();

                            // Only perform take click actions if we were in the pressed state
                            if (!focusTaken) {
                                // Use a Runnable and post this rather than calling
                                // performClick directly. This lets other visual state
                                // of the view update before click actions start.
                                if (mPerformClick == null) {
                                    mPerformClick = new PerformClick();
                                }
                              //调用View 的OnClickListener
                                if (!post(mPerformClick)) {
                                    performClick();
                                }
                            }
                        }

                        if (mUnsetPressedState == null) {
                            mUnsetPressedState = new UnsetPressedState();
                        }
                        //根系按压状态为false
                        if (prepressed) {
                            postDelayed(mUnsetPressedState,
                                    ViewConfiguration.getPressedStateDuration());
                        } else if (!post(mUnsetPressedState)) {
                            // If the post failed, unpress right now
                            mUnsetPressedState.run();
                        }

                        removeTapCallback();
                    }
                    break;

                case MotionEvent.ACTION_DOWN:
                    mHasPerformedLongPress = false;

                    if (performButtonActionOnTouchDown(event)) {
                        break;
                    }

                    // Walk up the hierarchy to determine if we're inside a scrolling container.
                    boolean isInScrollingContainer = isInScrollingContainer();

                    // For views inside a scrolling container, delay the pressed feedback for
                    // a short period in case this is a scroll.
                    //判断View是否在可以滚动的父View里面。
                    if (isInScrollingContainer) {
                        mPrivateFlags |= PFLAG_PREPRESSED;
                        if (mPendingCheckForTap == null) {
                            mPendingCheckForTap = new CheckForTap();
                        }
                        mPendingCheckForTap.x = event.getX();
                        mPendingCheckForTap.y = event.getY();
                        //TAP_TIMEOUT 判断用户是否想要滚动,而不是长按,相当于比不滚动的View多了500毫秒的时间
                        postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                    } else {
                        // Not inside a scrolling container, so show the feedback right away
                        setPressed(true, x, y);
                        //处理长按事件
                        checkForLongClick(0);
                    }
                    break;

                case MotionEvent.ACTION_CANCEL:
                    setPressed(false);
                    removeTapCallback();
                    removeLongPressCallback();
                    break;

                case MotionEvent.ACTION_MOVE:
                    drawableHotspotChanged(x, y);

                    // Be lenient about moving outside of buttons
                    if (!pointInView(x, y, mTouchSlop)) {
                        // Outside button
                        removeTapCallback();
                        if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                            // Remove any future long press/tap checks
                            removeLongPressCallback();

                            setPressed(false);
                        }
                    }
                    break;
            }

            return true;
        }

        return false;
    }

第7行先判断View的状态是否是DISABLED,如果是的话,会直接返回。第23行,判断View是否是可点击或者是可以长按的,如果满足条件,就进入下面Action判断,一最先执行的是ACTION_DOWN事件,106行通过调用checkForLongClick,处理长按事件:

private void checkForLongClick(int delayOffset) {
        if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
            mHasPerformedLongPress = false;

            if (mPendingCheckForLongPress == null) {
                mPendingCheckForLongPress = new CheckForLongPress();
            }
            mPendingCheckForLongPress.rememberWindowAttachCount();
            postDelayed(mPendingCheckForLongPress,
                    ViewConfiguration.getLongPressTimeout() - delayOffset);
        }
    }

通过post一个延迟为500毫秒的Runnable对象,等到Runnable执行的时候,检查View是否是按下状态,如果是的话,就当做一次长按事件,调用Activity里面监听的长按事件。
一般DOWN事件返回后,接下来是MOVE事件,在处理MOVE事件时,主要是移除长按长按的回调。MOVE事件结束后,就是UP事件处理Activity设置的点击事件,最终会调用:

public boolean performClick() {
        final boolean result;
        final ListenerInfo li = mListenerInfo;
        if (li != null && li.mOnClickListener != null) {
            playSoundEffect(SoundEffectConstants.CLICK);
            li.mOnClickListener.onClick(this);
            result = true;
        } else {
            result = false;
        }

        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
        return result;
    }

到这里,事件分发的理论分析完了。

你可能感兴趣的:(android,事件分发,事件传递)