Android事件分发机制

666 阅读15分钟

1、前言

  一直以来对于Android中事件分发机制的原理不是特别的理解,因此抽了几天的时间看了一下其中的源码部分,以加深对Android事件分发的理解和认识。当然在看的过程中也是借鉴了网上大牛的博客,有兴趣的可以看一下这篇博客https://blog.csdn.net/carson_ho/article/details/54136311写得特别的详细,不过里面的源码比较老是Andorid5.0,但是对于其中的原理理解还是有很大帮助的。

  该篇文章是以Andorid P的源码为目标进行Android事件分发机制理解的。

2、基础知识

  Android中将事件的细节(时间、事件类型、触摸位置、触摸的手指数量等)封装在了MotionEvent中,并将事件类型划分为了如下几类:

(1)MotionEvent.ACTION_UP:标志着事件结束;

(2)MotionEvent.ACTION_DOWN:标志着事件开始;

(3)MotionEvent.ACTION_CANCEL:非人为原因事件取消;

(4)MotionEvent.ACTION_MOVE:当事件开始之后,继续在屏幕上面移动。

  在Android中事件会按着Activity——>window——>DecorView——>ViewGroup——>View的顺序进行传递,而在事件的传递过程中涉及到了Android事件的分发以及事件的拦截,其涉及到的三个主要方法如下,也是我们在处理view滑动冲突的重要突破口:

(1)dispatchTouchEvent:用于调用事件拦截方法,判断是否需要拦截事件;并对事件进行分发处理,返回true表示当前view消费了该事件,在自定义view的时候一般不需要重写该方法;

(2)onInterceptTouchEvent:用于判断是否拦截事件,返回true则表示拦截事件,当前viewGroup消费事件;在自定义view处理滑动冲突的时候一般需要重写该方法;该方法只存在与ViewGroup中;

(3)onTouchEvent:对事件进行处理,返回true表示事件已经被消费了;自定义view重写该方法可以实现自己的滑动样式。

3、源码

  在了解了Android事件分发的基础知识之后,从事件分发的开始Activity的源码开始讲解。当一个事件发生时,事件首先会传递到Activity的dispatchTouchEvent函数中进行事件的分发。

3.1 Activity事件分发源码分析

 public boolean dispatchTouchEvent(MotionEvent ev) {
    //该处的条件判断一般情况下都为true,除非没有发生ACTION_DOWN
    if (ev.getAction() == MotionEvent.ACTION_DOWN) {
        //在Activity中该方法方法体为空,需要我们根据自己的需要是否重写
        //这个方法我也不是特别理解,只能把他的注释大致翻译一下:
        //如果Activity正在运行并且你想要知道用户正在以某种方式和你的设备进行交互的时候则可以实现该方法,该方法和
        //onUserLeaveHint用于协助Activity智能得管理通知栏并在合适的时候结束一个通知
        onUserInteraction();
    }
    //将事件传递给window进行处理,这里window的真正的实例是phoneWindow对象;在Activity的变量初始化
    //方法attach中生成了PhoneWindow实例对象
    if (getWindow().superDispatchTouchEvent(ev)) {
        return true;
    }
    return onTouchEvent(ev);
}
 /**
 *  当前Activity没有任何view处理该事件,也就是该事件发生在Window的边界处,则会调用该方法
 **/
 public boolean onTouchEvent(MotionEvent event) {
    //用于判断事件是否发生在window的边界处,如果是则结束当前Activity并且消费了该事件
    if (mWindow.shouldCloseOnTouch(this, event)) {
        finish();
        return true;
    }
    return false;
}

3.2 PhoneWindow事件分发源码分析

/**
*   在phoneWindow中又会将事件传递给DecorView进行处理
**/
public boolean superDispatchTouchEvent(MotionEvent event) {
    return mDecor.superDispatchTouchEvent(event);
}

3.3 DecorView事件分发源码分析

/**
*   在DecorView中又会将事件交个其父类进行处理,而DecorView则是继承了ViewGroup
**/
public boolean superDispatchTouchEvent(MotionEvent event) {
    return super.dispatchTouchEvent(event);
}

3.4 ViewGroup事件分发源码分析

  看源码会发现事件从ViewGroup才正式开始进行分发、拦截、处理。在ViewGroup的事件分发中做了很多事情,除了常规的事件拦截事件处理以外,还有多指触控等。具体处理如下:

(1)如果当前的是ACTION_DOWN事件,则会清除之前的所有事件并重置触摸状态;

(2)判断当前ViewGroup是否拦截事件;

(3)判断事件是否被取消或者被拦截;

(4)获取当前事件产生的指针id并清除该指针id早期的触摸目标以防止不同步;

(5)从后向前遍历哪一个子view可以消费事件;

(6)判断当前遍历到的子view是否可以接收事件(可见或者有动画效果且触摸点在子view范围内),如果不满足条件则直接跳过该子view;

(7)从上一次的触摸目标中查找是否存在当前的子view,如果存在则将当前指针id添加到所查找到的触摸目标中并直接跳出循环表示该view在上一次接收了事件,则将后续的所有事件都传递给该事件进行处理;

(8)调用dispatchTransformedTouchEvent方法并根据条件判断将事件是交给ViewGroup的父节点还是childView进行处理;

(9)如果没有找到子view处理事件,并且上一次有View处理事件,则将当前事件交由上一次处理事件的view进行处理;

(10)如果不存在view处理事件则将事件交由当前ViewGroup进行处理;如果存在vie处理事件,首先判断当前事件是否已经被处理,如果没有被处理则调用dispatchTransformedTouchEvent函数进行事件分发。

  @Override
 public boolean dispatchTouchEvent(MotionEvent ev) {
    //用于检测事件的合法性
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
    }
    if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
        ev.setTargetAccessibilityFocus(false);
    }
    boolean handled = false;
    //过滤MotionEvent
    if (onFilterTouchEventForSecurity(ev)) {
        final int action = ev.getAction();
        final int actionMasked = action & MotionEvent.ACTION_MASK;
        //如下当前触发的事件是ACTION_DOWN事件那么清除之前的所有事件
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            cancelAndClearTouchTargets(ev);
            resetTouchState();
        }
        final boolean intercepted;
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            //是否禁止拦截事件
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
                //是否拦截事件
                intercepted = onInterceptTouchEvent(ev);
                // 重新存储事件到MotionEvent中,以防止action被更改
                ev.setAction(action); 
            } else {
                //没有目标来处理事件,而且也不是一个新的事件(ACTION_DOWN),则进行拦截
                intercepted = false;
            }
        } else {
            intercepted = true;
        }
        if (intercepted || mFirstTouchTarget != null) {
            ev.setTargetAccessibilityFocus(false);
        }
        //检查是否取消事件
        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
        TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        //如果事件没有取消也没有被拦截则进行事件分发
        if (!canceled && !intercepted) {
            //该处理部分应该是针对Android中的无障碍功能的,注释大致意思如下:
            //如果事件以可访问性焦点为目标,我们将其提供给具有可访问性焦点的视图,如果它不处理,则会清除
            //该标识并将事件派发给所有的子view。因为这些事件非常罕见,因此我们一直检测该标识以避免保持该状态
            View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
                    ? findChildWithAccessibilityFocus() : null;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
                //获取该事件是哪个指针产生并获取当前指针的id
                final int actionIndex = ev.getActionIndex(); // always 0 for down
                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                        : TouchTarget.ALL_POINTER_IDS;
                //清除该手指对应id早期的触摸目标以防止不同步
                removePointersFromTouchTargets(idBitsToAssign);
                final int childrenCount = mChildrenCount;
                if (newTouchTarget == null && childrenCount != 0) {
                    //获取触摸位置
                    final float x = ev.getX(actionIndex);
                    final float y = ev.getY(actionIndex);
                    //查看哪一个childView可以接收此事件
                    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 (childWithAccessibilityFocus != null) {
                            if (childWithAccessibilityFocus != child) {
                                continue;
                            }
                            childWithAccessibilityFocus = null;
                            i = childrenCount - 1;
                        }
                        //判断当前view是否允许接收事件,即view处于VISIBLE状态或者存在动画
                        //或者触摸位置在view的范围内
                        if (!canViewReceivePointerEvents(child)
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }
                        //在mFirstTouchTarget中查找是否存在当前child View
                        newTouchTarget = getTouchTarget(child);
                        if (newTouchTarget != null) {
                            //已经找到接收事件的child view直接退出循环
                            newTouchTarget.pointerIdBits |= idBitsToAssign;
                            break;
                        }
                        resetCancelNextUpFlag(child);
                        //开始向下分发事件
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                            //记录上一次点击事件发生的时间、位置、ViewGroup下的哪一个view等
                            mLastTouchDownTime = ev.getDownTime();
                            if (preorderedList != null) {
                                for (int j = 0; j < childrenCount; j++) {
                                    if (children[childIndex] == mChildren[j]) {
                                        mLastTouchDownIndex = j;
                                        break;
                                    }
                                }
                            } else {
                                mLastTouchDownIndex = childIndex;
                            }
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            //通过childView和哪一个手指id构造TouchTarget,并让mFirstTouchTarget指向新的target
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }
                        ev.setTargetAccessibilityFocus(false);
                    }
                    if (preorderedList != null) preorderedList.clear();
                }
                if (newTouchTarget == null && mFirstTouchTarget != null) {
                    //没有找到子view接收事件,将指针id添加到最近的一次target中
                    newTouchTarget = mFirstTouchTarget;
                    while (newTouchTarget.next != null) {
                        newTouchTarget = newTouchTarget.next;
                    }
                    newTouchTarget.pointerIdBits |= idBitsToAssign;
                }
            }
        }
        //分发target
        if (mFirstTouchTarget == null) {
            // 没有找到可以接收事件的子view,将当前的ViewGroup当做普通的view进行事件分发
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            //如果已经准备好分发新的Target,则分发它。并且在需要的时候取消TouchTarget
            TouchTarget predecessor = null;
            TouchTarget target = mFirstTouchTarget;
            while (target != null) {
                final TouchTarget next = target.next;
                //如果已经分发的当前事件给子view则不重新进行分发
                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;
            }
        }
        //如果需要取消事件或者指针抬起或者指针在窗口或者vie区域移动但是没有按下(鼠标指针)
        //则重置触摸状态
        if (canceled
                || actionMasked == MotionEvent.ACTION_UP
                || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {
            //会将mFirstTouchTarget初始化为null
            resetTouchState();
        //如果有非主要的手指抬起(即抬起之后还有手指在屏幕上),则从TouchTargets移除发生事件的手指id
        } 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;
}

/**
*   将事件分发给具体的view
    return:是否处理事件
**/
 private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    final boolean handled;
    final int oldAction = event.getAction();
    //如果是取消事件,则将MotionEvent中action修改为ACTION_CANCEL
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
        event.setAction(MotionEvent.ACTION_CANCEL);
        //如果没有子view接收事件,则将事件传递给父view进行分发处理
        //如果有子view处理事件,则将事件传递给子view进行分发处理
        if (child == null) {
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        return handled;
    }
    final int oldPointerIdBits = event.getPointerIdBits();
    final int newPointerIdBits = oldPointerIdBits & desiredPointerIdBits;
    //如果一些原因导致反常的结束状态就像没有指针而发生的事件,则丢弃该事件
    if (newPointerIdBits == 0) {
        return false;
    }
    //如果触发事件发生的指针id与计算出来的指针id一致,那么我们不需要做任何转换
    final MotionEvent transformedEvent;
    if (newPointerIdBits == oldPointerIdBits) {
        if (child == null || child.hasIdentityMatrix()) {
            if (child == null) {
                handled = super.dispatchTouchEvent(event);
            } else {
                final float offsetX = mScrollX - child.mLeft;
                final float offsetY = mScrollY - child.mTop;
                event.offsetLocation(offsetX, offsetY);
                handled = child.dispatchTouchEvent(event);
                event.offsetLocation(-offsetX, -offsetY);
            }
            return handled;
        }
        transformedEvent = MotionEvent.obtain(event);
    } else {
        transformedEvent = event.split(newPointerIdBits);
    }
    //如果触发事件发生的指针id与计算出来的指针id不一致,那么我们就需要做转换
    if (child == null) {
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        //如果子view不是单位矩阵,则执行转换并将事件传递给子view进行分发
        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;
}

3.5 View事件分发源码分析

  在view的dispatchTouchEvent函数中,做了如下的处理:

(1)首先会判断该事件是否是由可访问性焦点视图处理(可能是为了支持Android中的无障碍功能);

(2)判断当前view是否设置了OnTouchListener,如果设置了则直接调用Listener中的onTouch方法,如果返回true则直接将是否消费事件flag设置为true;

(3)根据条件判断是否调用View中定义的onTouch方法。

 public boolean dispatchTouchEvent(MotionEvent event) {
    // 如果事件需要被可访问性焦点视图处理
    if (event.isTargetAccessibilityFocus()) {
        if (!isAccessibilityFocusedViewOrHost()) {
            return false;
        }
        event.setTargetAccessibilityFocus(false);
    }
    boolean result = false;
    if (mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onTouchEvent(event, 0);
    }
    final int actionMasked = event.getActionMasked();
    //如果事件是ACTION_DOWN则停止嵌套滑动
    if (actionMasked == MotionEvent.ACTION_DOWN) {
        stopNestedScroll();
    }
    //过滤不合法的事件
    if (onFilterTouchEventForSecurity(event)) {
        if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
            result = true;
        }
        ListenerInfo li = mListenerInfo;
        //如果设置了touch监听器,并且当前view可点击,就调用监听器的onTouch方法,如果ontouch方法返回true
        //则将result置为true表示消费的该事件
        if (li != null && li.mOnTouchListener != null
                && (mViewFlags & ENABLED_MASK) == ENABLED
                && li.mOnTouchListener.onTouch(this, event)) {
            result = true;
        }
        //如果事件没有被消费则调用view中的onTouch方法,如果返回true,则将result置为true表示消费了该事件
        if (!result && onTouchEvent(event)) {
            result = true;
        }
    }
    if (!result && mInputEventConsistencyVerifier != null) {
        mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
    }
    if (actionMasked == MotionEvent.ACTION_UP ||
            actionMasked == MotionEvent.ACTION_CANCEL ||
            (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
        stopNestedScroll();
    }
    return result;
}

在view的onTouch方法中做了如下的处理:

(1)获取发生事件的位置;

(2)判断view是否可点击以及是否可用,如果view不可用则之间返回view是否可点击以表示事件是否被消费;

(3)判断是否设置了view扩大点击范围实例对象,如果设置了则将事件交由该对象进行处理,并且点击事件也发生在mTouchDelegate对象中所设置的view上;

(4)如果view可点击或者可长按则处理ACTION_UP、ACTION_DOWN、ACTION_CANCEL、ACTION_MOVE事件;

(5)在ACTION_DOWN事件中会判断当前view是否嵌套于滚动容器中,如果是则延迟检测长按事件,如果不是则立马检测长按事件,如果设置了长按事件则直接执行长按事件;

(6)在ACTION_UP事件中首先会清除在ACTION_DOWN事件中所设置的事件检测;

(7)如果没有处理长按事件并且没有获取就焦点则开始处理点击事件;

(8)在ACTION_CANCEL事件中则清除设置的事件检测 并初始化各个变量;

(9)在ACTION_MOVE事件中会检测当前的触摸位置是否在View范围当中,如果在则清除所设置的事件检测等。

 public boolean onTouchEvent(MotionEvent event) {
    final float x = event.getX();
    final float y = event.getY();
    final int viewFlags = mViewFlags;
    final int action = event.getAction();
    //获取View是否可以点击
    final boolean clickable = ((viewFlags & CLICKABLE) == CLICKABLE
            || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
            || (viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE;
    /判断view是否可用
    if ((viewFlags & ENABLED_MASK) == DISABLED) {
        if (action == MotionEvent.ACTION_UP && (mPrivateFlags & PFLAG_PRESSED) != 0) {
            setPressed(false);
        }
        mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
        //一个不可用的view但是可以点击仍然消费这个事件,只是没有任何效果而已
        return clickable;
    }
    //用于扩大view的触摸范围
    if (mTouchDelegate != null) {
        if (mTouchDelegate.onTouchEvent(event)) {
            return true;
        }
    }
    //如果当前view可以点击或者在hove和长按下可以显示ToolTip
    if (clickable || (viewFlags & TOOLTIP) == TOOLTIP) {
        switch (action) {
            case MotionEvent.ACTION_UP:
                //从flag中移除用户触摸屏幕标签
                mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                if ((viewFlags & TOOLTIP) == TOOLTIP) {
                    handleTooltipUp();
                }
                //清除设置检测view是否支持点击和长按事件
                if (!clickable) {
                    removeTapCallback();
                    removeLongPressCallback();
                    mInContextButtonPress = false;
                    mHasPerformedLongPress = false;
                    mIgnoreNextUpEvent = false;
                    break;
                }
                //判断view是否嵌套于滚动容器中并且还没有检测tap事件
                boolean prepressed = (mPrivateFlags & PFLAG_PREPRESSED) != 0;
                //如果view处于pressed状态或者prepressed
                if ((mPrivateFlags & PFLAG_PRESSED) != 0 || prepressed) {
                    boolean focusTaken = false;
                    //如果当前view没有获取焦点且可以获取焦点且在进入到touch mode下可以获取焦点则获取焦点
                    if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
                        focusTaken = requestFocus();
                    }
                    if (prepressed) {
                        setPressed(true, x, y);
                    }
                    //如果没有处理长按事件,那么该事件则是点击事件
                    if (!mHasPerformedLongPress && !mIgnoreNextUpEvent) {
                        //移除检测长按事件
                        removeLongPressCallback();
                        if (!focusTaken) {
                            if (mPerformClick == null) {
                                mPerformClick = new PerformClick();
                            }
                            //通过post的方式处理点击事件如果post调用失败则直接处理点击事件
                            if (!post(mPerformClick)) {
                                performClick();
                            }
                        }
                    }
                    if (mUnsetPressedState == null) {
                        mUnsetPressedState = new UnsetPressedState();
                    }
                    if (prepressed) {
                        postDelayed(mUnsetPressedState,
                                ViewConfiguration.getPressedStateDuration());
                    } else if (!post(mUnsetPressedState)) {
                        // If the post failed, unpress right now
                        mUnsetPressedState.run();
                    }
                    removeTapCallback();
                }
                mIgnoreNextUpEvent = false;
                break;
            case MotionEvent.ACTION_DOWN:
                if (event.getSource() == InputDevice.SOURCE_TOUCHSCREEN) {
                    //用于标识用户现在正在触摸屏幕并且现在只用于ToolTip定位
                    mPrivateFlags3 |= PFLAG3_FINGER_DOWN;
                }
                mHasPerformedLongPress = false;
                //如果view不可点击则检查长按
                if (!clickable) {
                    checkForLongClick(0, x, y);
                    break;
                }
                //该处一般的设备都返回false,它主要处理如果此view或者其父view响应右键菜单,那么此事件就会被消耗掉
                if (performButtonActionOnTouchDown(event)) {
                    break;
                }
                // 沿着view所处的层次检测当前view是否处于滚动容器中
                boolean isInScrollingContainer = isInScrollingContainer();
                //如果当前view处于滚动容器中,则延迟反馈pressed一定时间
                if (isInScrollingContainer) {
                    //为flag添加PREPRESSED(父类可能会进行滑动处理,在过了一段时间之后如果父类没有拦截事件,
                    //则认为事件传递到了当前view)
                    //并在一段时间之后检查是否支持长按事件,如果支持长按事件则执行代码中的
                    //performLongClick方法等并调用已经注册的监听器中的onLongclick方法
                    mPrivateFlags |= PFLAG_PREPRESSED;
                    if (mPendingCheckForTap == null) {
                        mPendingCheckForTap = new CheckForTap();
                    }
                    mPendingCheckForTap.x = event.getX();
                    mPendingCheckForTap.y = event.getY();
                    postDelayed(mPendingCheckForTap, ViewConfiguration.getTapTimeout());
                } else {
                    //没有在滚动容器中,不用延迟反馈pressed,并设置pressed状态为true
                    setPressed(true, x, y);
                    checkForLongClick(0, x, y);
                }
                break;
            case MotionEvent.ACTION_CANCEL:
                //设置pressed状态为false
                if (clickable) {
                    setPressed(false);
                }
                //从队列中移除mPendingCheckForTap
                removeTapCallback();
                //从队列中移除mPendingCheckForLongPress
                removeLongPressCallback();
                mInContextButtonPress = false;
                mHasPerformedLongPress = false;
                mIgnoreNextUpEvent = false;
                //从flag中移除标识用户触摸屏幕标签
                mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                break;
            case MotionEvent.ACTION_MOVE:
                if (clickable) {
                    drawableHotspotChanged(x, y);
                }
                // 判断当前触摸点是否还在view的范围内,如果不是则移除所有的事件检测
                if (!pointInView(x, y, mTouchSlop)) {
                    removeTapCallback();
                    removeLongPressCallback();
                    if ((mPrivateFlags & PFLAG_PRESSED) != 0) {
                        setPressed(false);
                    }
                    mPrivateFlags3 &= ~PFLAG3_FINGER_DOWN;
                }
                break;
        }
        return true;
    }
    return false;
}

4、总结

  从Android整体的事件分发流程可以看出来:

  (1)如果一个view处理了ACTION_DOWN事件之后,那么后续的事件会全部交由该view进处理,这在ViewGroup中的dispatchTouchEvent方法中可以体现;

  (2)如果子view不处理事件,则会将该事件交由父ViewGroup进行处理,如果父ViewGroup不进行处理则会继续向上进行传递直到传递到Activity中。在ViewGroup遍历子view处理事件过程中就有所体现,如果子view处理事件返回的false则说明该事件子view没有处理,如果遍历完所有的子view都返回false,那么mFirstTouchTarget=null,说明ViewGroup下的所有子view都没有消费事件,那么当前ViewGroup就会处理该事件。

  (3)如果一个事件在Activity中的DecorView所有子view都不进行处理,那么该事件会被Activity中的onTouch方法处理掉;

  (4)除了处理普通的事件传递之外,还处理了多指触控,无障碍触摸等事件