Android事件分发源码阅读笔记
触摸事件流
Down --> Move --> Move --> Up
Down --> Pointer_Down --> Move --> Move --> Pointer_Up --> Up
事件分发流程图
图片摘自:一文读懂Android View事件分发机制
在这里插入图片描述
源码注释
ViewGroup.java
    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
            // 我们已经处于这种状态,假设我们的父View也是如此
            return;
        }
        if (disallowIntercept) {
            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
        } else {
            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
        }
        // 把它传递给我们的父View
        if (mParent != null) {
            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onTouchEvent(ev, 1);
        }
        //如果事件以无障碍焦点的View为目标,并且此View就是那个无障碍焦点View则开始
        //正常事件分发。
        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;
            // 处理初始的ACTION_DOWN
            if (actionMasked == MotionEvent.ACTION_DOWN) {
                //开始新的触摸手势时,丢弃(重置)所有先前的状态。
                //框架可能已经丢弃了上一个手势的up或cancel事件
                //由于应用程序切换,ANR或其他一些状态更改。
                cancelAndClearTouchTargets(ev);
                resetTouchState();
            }
            // 检查该ViewGroup是否拦截触摸事件
            final boolean intercepted;
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {//当触摸事件的Action为ACTION_DOWN,或者mFirstTouchTarget != null(表示子View已经消费了Down事件成为TouchTarget,这个是后续发送给子View的触摸事件(例如Move,Up))
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                if (!disallowIntercept) {// 允许此ViewGroup拦截
                    // Down事件和发送给TouchTarget的事件都会在这里检测是否拦截
                    intercepted = onInterceptTouchEvent(ev);
                    // 恢复Action以防止被更改
                    ev.setAction(action); 
                } else {
                    // 不允许此ViewGroup拦截
                    intercepted = false;
                }
            } else {
                // 进入这里表明了是Down之后的事件,而TouchTarget为Null,说明子View没有消费之前的Down事件,而是被
                // 该ViewGroup拦截消费了。所以后续事件继续被该ViewGroup消费,设置intercepted=true就可以让该ViewGroup的onTouchEvent处理该事件
                intercepted = true;
            }
            //如果该ViewGroup拦截事件,则启动正常事件分发。 同样如果已经存在的一个
            //正在处理手势的子View,执行正常的事件分发。
            if (intercepted || mFirstTouchTarget != null) {
                ev.setTargetAccessibilityFocus(false);
            }
            // 检查 canceled
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;
            // 如果需要,更新pointer down的触摸目标列表。 
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            if (!canceled && !intercepted) {
                //如果事件的目标是无障碍焦点,如果不处理它,我们会将其提供给
                //具有无障碍焦点的View。
                //我们清除标志并像往常一样将事件发送给所有子View。
                //我们正在查找无障碍焦点宿主以避免保留
                //状态,因为这些事件非常罕见。
                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(); // 0总是表示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);
                        //找到可以接收事件的子View。
                        //从前到后扫描子View。
                        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);
                            //如果有一个具有无障碍焦点的View,我们需要它
                            //首先获取事件并且如果没有处理该事件,我们将执行
                            //正常分发。
                            if (childWithAccessibilityFocus != null) {
                                if (childWithAccessibilityFocus != child) {
                                    continue;
                                }
                                childWithAccessibilityFocus = null;
                                i = childrenCount - 1;
                            }
                            if (!canViewReceivePointerEvents(child)
                                    || !isTransformedTouchPointInView(x, y, child, null)) {
                                ev.setTargetAccessibilityFocus(false);
                                continue;
                            }
                            newTouchTarget = getTouchTarget(child);
                            if (newTouchTarget != null) {
                                //子View已经在其范围内接受了触摸。
                                //除了正在处理的触摸点之外,还给它新的触摸点(多点触控)。
                                newTouchTarget.pointerIdBits |= idBitsToAssign;
                                break;
                            }
                            resetCancelNextUpFlag(child);
                            if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                                // 子View想在它的范围内接收触摸事件
                                mLastTouchDownTime = ev.getDownTime();
                                if (preorderedList != null) {
                                    // childIndex指向预先排序列表,找到原始索引
                                    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;
                            }
                            // 无障碍焦点不处理这个事件,因此清除标志并且对所有的子View进行的正常事件分发
                            ev.setTargetAccessibilityFocus(false);
                        }
                        if (preorderedList != null) preorderedList.clear();
                    }
                    if (newTouchTarget == null && mFirstTouchTarget != null) {
                        //没有找到子View来接收这个事件。
                        //将指针分配给最近添加的目标。
                        newTouchTarget = mFirstTouchTarget;
                        while (newTouchTarget.next != null) {
                            newTouchTarget = newTouchTarget.next;
                        }
                        newTouchTarget.pointerIdBits |= idBitsToAssign;
                    }
                }
            }
            // 分发事件到触摸目标
            if (mFirstTouchTarget == null) {
                // 没有触摸目标,因此将此ViewGroup作为普通View对待.
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                //发送到触摸目标。如果我们已经发送到这个新的触摸目标,则排除。必要时取消触摸目标。
                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 (prtargetedecessor == null) {
                                mFirstTouchTarget = next;
                            } else {
                                predecessor.next = next;
                            }
                            target.recycle();
                            target = next;
                            continue;
                        }
                    }
                    predecessor = target;
                    target = next;
                }
            }
            // 如果需要,更新指针up或cancel的触摸目标列表。
            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;
    }
    /**
     * 将动作事件(MotionEvent)转换到特定子View的坐标空间,
     * 过滤掉不相关的指针(每个手指对应一个指针)ID,并在必要时覆盖其操作。 
     * 如果child为null,则假定MotionEvent将发送给ViewGroup本身。
     */
    private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
            View child, int desiredPointerIdBits) {
        final boolean handled;
        //取消手势(MotionEvent.ACTION_CANCEL)是一种特殊情况。 我们不需要执行任何转换
        //或过滤。 重要的是动作,而不是内容。
        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;
        }
        // 计算要传递的指针数。
        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;
        }
        //如果指针的数量相同,并且我们不需要执行任何
        //不可逆的转换操作,这时,我们可以重复使用该动作事件进行分发,
        //只要我们小心地还原我们所做的任何更改即可。
        //否则,我们就需要拷贝一份该动作事件。
        final MotionEvent transformedEvent;
        if (newPointerIdBits == oldPointerIdBits) {
            //child为null,或者child的转换矩阵是单位矩阵。执行完就直接返回了
            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);//这里有两种,一种是View的,另一种是ViewGroup的
                    event.offsetLocation(-offsetX, -offsetY);
                }
                return handled;
            }
            //拷贝动作事件
            transformedEvent = MotionEvent.obtain(event);
        } else {
            transformedEvent = event.split(newPointerIdBits);
        }
        // 执行任何必要的转换和分发.
        if (child == null) {
            //因为子View为null,所以直接分发给自己。super.dispatchTouchEvent对应的是View
            //的dispatchTouchEvent方法
            handled = super.dispatchTouchEvent(transformedEvent);
        } else {
            final float offsetX = mScrollX - child.mLeft;
            final float offsetY = mScrollY - child.mTop;
            transformedEvent.offsetLocation(offsetX, offsetY);
            if (! child.hasIdentityMatrix()) {//child的转换矩阵不是单位矩阵
                //通过反向矩阵恢复动作事件
                transformedEvent.transform(child.getInverseMatrix());
            }
            handled = child.dispatchTouchEvent(transformedEvent);
        }
        // 完毕.
        transformedEvent.recycle();
        return handled;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    207
    208
    209
    210
    211
    212
    213
    214
    215
    216
    217
    218
    219
    220
    221
    222
    223
    224
    225
    226
    227
    228
    229
    230
    231
    232
    233
    234
    235
    236
    237
    238
    239
    240
    241
    242
    243
    244
    245
    246
    247
    248
    249
    250
    251
    252
    253
    254
    255
    256
    257
    258
    259
    260
    261
    262
    263
    264
    265
    266
    267
    268
    269
    270
    271
    272
    273
    274
    275
    276
    277
    278
    279
    280
    281
    282
    283
    284
    285
    286
    287
    288
    289
    290
    291
    292
    293
    294
    295
    296
    297
    298
    299
    300
    301
    302
    303
    304
    305
    306
    307
    308
    309
    310
    311
    312
    313
    314
    315
    316
    317
    318
View.java
    /**
      *将触摸屏动作事件向下传递到目标View,或当此View是目标时,传递到此View
      * @param event 要调度的动作事件。
      * @return 如果事件由此View处理,则为True,否则为false。
     */
    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);
        }
        // 获取动作的int值
        final int actionMasked = event.getActionMasked();
        // 如果动作是按下
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // 新手势的防御性清理。停止正在进行的嵌套滚动
            stopNestedScroll();
        }
        //过滤触摸事件以应用安全策略。True表示应该被分发,False表示应该丢弃
        if (onFilterTouchEventForSecurity(event)) {
            if ((mViewFlags & ENABLED_MASK) == ENABLED && handleScrollBarDragging(event)) {
                result = true;
            }
 
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    // 如果这个View没有调用setEnabled(false),这里应该是相等的
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    //如果调用了setOnTouchListener(),这里就会回调该Listener
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;//说明OnTouchListener.onTouch方法消费了触摸事件
            }
            //如果事件没有被onTouch方法消费,则传递给View的onTouchEvent方法
            if (!result && onTouchEvent(event)) {
                result = true;
            }
        }
        // 如果事件没有被消费,并且有用于调试目的的一致性验证对象
        if (!result && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(event, 0);
        }
        //如果这是手势的结束,则在嵌套滚动后清理;
        //如果我们已经查验了ACTION_DOWN,但我们不想要其余的
        //手势,我们也取消
        if (actionMasked == MotionEvent.ACTION_UP ||
                actionMasked == MotionEvent.ACTION_CANCEL ||
                (actionMasked == MotionEvent.ACTION_DOWN && !result)) {
            //停止嵌套滚动
            stopNestedScroll();
        }
        return result;
    }
    /**
     * 过滤触摸事件以应用安全策略
     *
     * @param event 要被过滤的触摸事件
     * @return True 如果触摸事件应该被分发, false 如果触摸事件应该被丢弃.
     *
     * @see #getFilterTouchesWhenObscured
     */
    public boolean onFilterTouchEventForSecurity(MotionEvent event) {
           //FILTER_TOUCHES_WHEN_OBSCURED 表示View应在其窗口被遮挡时过滤触摸事件。
        if ((mViewFlags & FILTER_TOUCHES_WHEN_OBSCURED) != 0
                    //FLAG_WINDOW_IS_OBSCURED 
                    // 该标志表示接收到该触摸事件的窗口被其上方的另一个可见窗口部分或全部遮挡。 
                    // 即使事件没有直接通过遮挡区域,该标志也设置为true。 对安全敏感的应用程序可以检查此标志,
                    // 以识别恶意应用程序可能已覆盖其部分内容的情况,以便误导用户或劫持触摸。 
                    // 适当的回应可能是放弃可疑的触摸或采取额外的预防措施来确认用户的实际意图。
                && (event.getFlags() & MotionEvent.FLAG_WINDOW_IS_OBSCURED) != 0) {
            // Window 被遮挡, 丢弃这个触摸事件.
            return false;
        }
        return true;
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
Activity.java
    /**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {//View的事件分发从这里开始。 ViewGroup --> ... --> View
            return true;
        }
        return onTouchEvent(ev);//如果没有View消费该Touch事件,则分发到Activity的onTouchEvent方法
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
getAction() 返回 pointer id 和 action (例如: up, down, move)
getActionMasked() 只返回action (例如: up, down, move)
小结:
    完整的事件流是怎么样的?
    一个完整的事件流的都是从Down开始,然后到Cancel/Up结束,中间会经历数个Move事件。
    如果没有View去消费Down事件,接下来的其它事件会怎么样?
    如果子View没有消费Down事件,则会调用父ViewGroup的onTouchEvent来处理该事件,如果该ViewGroup也没有消费,就会调用该ViewGroup的父ViewGroup来处理该事件。如此往复循环一直到最顶层的DecorView。接下来的Move,Up之类的事件将全部被DecorView拦截消费。一直持续到下一次的Down事件。这里还有一点,如果DecorView拦截的事件也没有消费,会调用Activity的onTouchEvent方法。
    如何解决事件冲突?
    首先肯定需要有一个判断逻辑来确定应该由父ViewGroup消费Touch事件,还是由子View消费Touch事件。那如何进行切换呢?通过上面的分发流程图我们知道,如果父ViewGroup拦截了Down事件,那子View直到下一个Down事件之前就再也接收不到其它Touch事件。因此,Down事件父ViewGroup肯定是不能拦截的。那子View接收并消费Down事件后,其余的事件还是一层层传递到该子View,所以必然会经过父ViewGroup,而这个时候就会经过onInterceptTouchEvent,当该方法返回true的时候,父View就会拦截后续事件,直到Up事件。拦截的事件都会传递到父ViewGroup的onTouchEvent。
    解决事件冲突的办法,就是在父ViewGrouponInterceptTouchEvent编写判断逻辑,通过返回true来拦截。具体的实践讲解可以参考这篇文章:一文解决Android View滑动冲突
--------------------- 
作者:心中要有一片海 
来源:CSDN 
原文:https://blog.csdn.net/lylwo317/article/details/86716983 
版权声明:本文为博主原创文章,转载请附上博文链接!
 
                    
                
 
                
            
         浙公网安备 33010602011771号
浙公网安备 33010602011771号