Android事件分发源码阅读笔记

时间:2019-02-16
本文章向大家介绍Android事件分发源码阅读笔记,主要包括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;
    }

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

		//如果这是手势的结束,则在嵌套滚动后清理;