ViewGroup的事件分发源码分析

2018-08-04  本文已影响52人  Utte

ViewGroup # dispatchTouchEvent

@Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        // 如果检查对象不空就检查事件,在View中也有这一步,但是传入的嵌套级别为0。
        // 注释中有说如果事件已经被嵌套级别较高的View检查过了就不会再检查一遍了。
        // 所以这里对事件已经进行过1等级的处理了,分发给下级View时就不会重新检查了。
        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.
        // 如果事件目标是焦点View,并且这个View就处于焦点或是焦点View的嵌套父View
        if (ev.isTargetAccessibilityFocus() && isAccessibilityFocusedViewOrHost()) {
            // 设置不需要判断,继续正常分发工作,以后就不需要重复判断了。
            ev.setTargetAccessibilityFocus(false);
        }

        boolean handled = false;
        // 如果设置了窗口遮挡时过滤,并且确实有窗口遮挡,就过滤。
        // 返回false表示过滤,这个过滤是在构造器通过判断style来设置的。
        // 如果不过滤,就进入下面的操作。
        if (onFilterTouchEventForSecurity(ev)) {
            final int action = ev.getAction();
            final int actionMasked = action & MotionEvent.ACTION_MASK;

            // Handle an initial down.
            // 如果是ACTION_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.
                // private void cancelAndClearTouchTargets(MotionEvent event) {
                //    // 如果上个事件序列中消耗事件的View的引用还没来得及回收
                //    if (mFirstTouchTarget != null) {
                //        boolean syntheticEvent = false;
                //        // 如果事件为空,就创造一个ACTION_CANCEL事件
                //        // 从disptachTouchEvent()中调用的不会进入这个方法块,因为已经判断了为ACTION_DOWN
                //        if (event == null) {
                //            final long now = SystemClock.uptimeMillis();
                //            event = MotionEvent.obtain(now, now,
                //                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
                //            event.setSource(InputDevice.SOURCE_TOUCHSCREEN);
                //            syntheticEvent = true;
                //        }
                //        for (TouchTarget target = mFirstTouchTarget; target != null; target = target.next) {
                //            // 如果参数View设置了PFLAG_CANCEL_NEXT_UP_EVENT标识位,就取消
                //            // private static boolean resetCancelNextUpFlag(@NonNull View view) {
                //            //     if ((view.mPrivateFlags & PFLAG_CANCEL_NEXT_UP_EVENT) != 0) {
                //            //         view.mPrivateFlags &= ~PFLAG_CANCEL_NEXT_UP_EVENT;
                //            //         return true;
                //            //     }
                //            //     return false;
                //            // }
                //            resetCancelNextUpFlag(target.child);
                //            // 这里主要是想让所有链上的所有View都传递一个ACTION_CANCEL
                //            // private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
                //            //         View child, int desiredPointerIdBits) {
                //            //     final boolean handled;
                //            //     final int oldAction = event.getAction();
                //            //     // 需要取消或是事件类型为ACTION_CANCEL
                //            //     if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
                //            //         // 传参需要取消,不管event是什么类型,都先设置一个ACTION_CANCEL
                //            //         event.setAction(MotionEvent.ACTION_CANCEL);
                //            //         // 如果没有子View了,就交给View来处理
                //            //         if (child == null) {
                //            //             handled = super.dispatchTouchEvent(event);
                //            //         } else {
                //            //             // 如果有就传递给子View继续分发
                //            //             handled = child.dispatchTouchEvent(event);
                //            //         }
                //            //         // 再设置回原来的样子
                //            //         event.setAction(oldAction);
                //            //         return handled;
                //            //     }
                //            //     // ......
                //            // }
                //            dispatchTransformedTouchEvent(event, true, target.child, target.pointerIdBits);
                //        }
                //        // private void clearTouchTargets() {
                //        //     TouchTarget target = mFirstTouchTarget;
                //        //     if (target != null) {
                //        //         // 回收链上的每一个TouchTarget
                //        //         do {
                //        //             TouchTarget next = target.next;
                //        //             target.recycle();
                //        //             target = next;
                //        //         } while (target != null);
                //        //         mFirstTouchTarget = null;
                //        //     }
                //        // }
                //        clearTouchTargets();
                //        // 如果事件为空就回收事件
                //        if (syntheticEvent) {
                //            event.recycle();
                //        }
                //    }
                //}
                cancelAndClearTouchTargets(ev);
                // private void resetTouchState() {
                //     回收TouchTarget
                //     clearTouchTargets();
                //     撤销PFLAG_CANCEL_NEXT_UP_EVENT
                //     resetCancelNextUpFlag(this);
                //     撤销FLAG_DISALOW_INTERCEPT
                //     所以这个不允许拦截的标志位对ACTION_DOWN事件无效
                //     mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
                //     mNestedScrollAxes = SCROLL_AXIS_NONE;
                // }
                resetTouchState();
            }

            // Check for interception.
            // 检查是否拦截
            final boolean intercepted;
            // 如果是ACTION_DOWN或者事件序列的前几个事件已经有子View消耗了
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || mFirstTouchTarget != null) {
                final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
                // 没有设置不允许拦截才能去判断是否拦截
                if (!disallowIntercept) {
                    // 调用onInterceptTouchEvent判断是否需要拦截
                    // 默认返回false,不拦截
                    intercepted = onInterceptTouchEvent(ev);
                    // 防止操作中将event的类型改变了
                    ev.setAction(action); // restore action in case it was changed
                } else {
                    // 如果设置了,就不拦截
                    intercepted = false;
                }
            } else {
                // 没有消耗而且也不是ACTION_DOWN事件,就拦截。
                // 会有两种情况,
                // 一种是一开始该ViewGroup就拦截了ACTION_DOWN事件
                // 或者子View没有消耗前几个事件,它们对于之后的事件也不感兴趣,所以拦截
                // 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.
            // 如果拦截或这已经有View拦截了事件,就开始正常的分发工作
            if (intercepted || mFirstTouchTarget != null) {
                ev.setTargetAccessibilityFocus(false);
            }

            // Check for cancelation.
            // 如果设置了FLAG_CANCEL_NEXT_UP_EVENT就取消这个设置
            // 或者该事件是cancel事件
            final boolean canceled = resetCancelNextUpFlag(this)
                    || actionMasked == MotionEvent.ACTION_CANCEL;

            // Update list of touch targets for pointer down, if needed.
            // 是否拆分事件,默认可拆分
            // 注释说在必要的情况下,可以将一个事件分解传递给多个子View
            final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
            TouchTarget newTouchTarget = null;
            boolean alreadyDispatchedToNewTouchTarget = false;
            // 如果事件没有取消且不拦截
            if (!canceled && !intercepted) {
                
                
                
                
                // ......
            
                
                
                
                
            }

            // Dispatch to touch targets.
            // 如果事件序列中还没有分发消耗的事件对象
            if (mFirstTouchTarget == null) {
                // No touch targets so treat this as an ordinary view.
                // 尝试自己消耗事件
                handled = dispatchTransformedTouchEvent(ev, canceled, null,
                        TouchTarget.ALL_POINTER_IDS);
            } else {
                // Dispatch to touch targets, excluding the new touch target if we already
                // dispatched to it.  Cancel touch targets if necessary.
                TouchTarget predecessor = null;
                TouchTarget target = mFirstTouchTarget;
                while (target != null) {
                    final TouchTarget next = target.next;
                    // 遍历到消耗事件的子View了,置handled为true
                    if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                        handled = true;
                    } else {
                        // 如果设置了PFLAG_CANCEL_NEXT_UP_EVENT就取消  或  需要拦截 返回true
                        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                                || intercepted;
                        // 如果cancelChild为true,就让该child分发一个cancel事件
                        // 如果cancelChild为false,就让该child分发该事件
                        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) {
                // 如果是取消或up事件或鼠标移动事件,就清空所有状态
                resetTouchState();
            } else if (split && actionMasked == MotionEvent.ACTION_POINTER_UP) {
                // 当有手指抬起时,清空这个手指之前触碰点的所有信息
                final int actionIndex = ev.getActionIndex();
                final int idBitsToRemove = 1 << ev.getPointerId(actionIndex);
                // private void removePointersFromTouchTargets(int pointerIdBits) {
                //    TouchTarget predecessor = null;
                //    TouchTarget target = mFirstTouchTarget;
                //    while (target != null) {
                //        final TouchTarget next = target.next;
                //        if ((target.pointerIdBits & pointerIdBits) != 0) {
                //            target.pointerIdBits &= ~pointerIdBits;
                //            // 如果除了需要删除的值之外就没有别的了,就将这个结点删除并回收
                //            if (target.pointerIdBits == 0) {
                //                if (predecessor == null) {
                //                    mFirstTouchTarget = next;
                //                } else {
                //                    predecessor.next = next;
                //                }
                //                target.recycle();
                //                target = next;
                //                continue;
                //            }
                //        }
                //        predecessor = target;
                //        target = next;
                //    }
                //}
                removePointersFromTouchTargets(idBitsToRemove);
            }
        }
        // 发起检查
        if (!handled && mInputEventConsistencyVerifier != null) {
            mInputEventConsistencyVerifier.onUnhandledEvent(ev, 1);
        }
        return handled;
    }

非取消事件且不拦截的分发消耗过程

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设置了FLAG_TARGET_ACCESSIBILITY_FOCUS
    // 就找到下一层正真处焦点的View
    // private View findChildWithAccessibilityFocus() {
    //    ViewRootImpl viewRoot = getViewRootImpl();
    //    if (viewRoot == null) {
    //        return null;
    //    }
    //    View current = viewRoot.getAccessibilityFocusedHost();
    //    if (current == null) {
    //        return null;
    //    }
    //    ViewParent parent = current.getParent();
    //    // 一层一层向上找
    //    // 直到找到一个View为该ViewGroup的child并且还嵌套着焦点View
    //    while (parent instanceof View) {
    //        if (parent == this) {
    //            return current;
    //        }
    //        current = (View) parent;
    //        parent = current.getParent();
    //    }
    //    // 如果没有找到,说明该ViewGroup没有嵌套焦点View
    //    return null;
    //}
    View childWithAccessibilityFocus = ev.isTargetAccessibilityFocus()
            ? findChildWithAccessibilityFocus() : null;
    // 如果是ACTION_DOWN
    // 或可分离且非第一个手指按下的情况,比如第一个手指按下并没有松开,此时又一只手指按下
    // 或者是鼠标移动且并未点击,4.0后会监听鼠标移动
    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
        // 如果可分离就通过索引获取的值左移一位
        // 否则取常量 -1
        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.
        // 清除这个id早前的作用
        removePointersFromTouchTargets(idBitsToAssign);
        final int childrenCount = mChildrenCount;
        if (newTouchTarget == null && childrenCount != 0) {
            final float x = ev.getX(actionIndex);
            final float y = ev.getY(actionIndex);
            // Find a child that can receive the event.
            // Scan children from front to back.
            // public ArrayList<View> buildTouchDispatchChildList() {
            //     return buildOrderedChildList();
            // }
            // ArrayList<View> buildOrderedChildList() {
            //     final int childrenCount = mChildrenCount;
            //     // 如果子View数小于等于1
            //     // 或者遍历子view不存在子getZ()不为0的view,也就是没有都没有高度时
            //     // z属性用于描述视图距离它父视图的高度
            //     if (childrenCount <= 1 || !hasChildWithZ()) return null;
            //     if (mPreSortedChildren == null) {
            //         mPreSortedChildren = new ArrayList<>(childrenCount);
            //     } else {
            //         // callers should clear, so clear shouldn't be necessary, but for safety...
            //         mPreSortedChildren.clear();
            //         // 重新分配日大小
            //         mPreSortedChildren.ensureCapacity(childrenCount);
            //     }
            //     final boolean customOrder = isChildrenDrawingOrderEnabled();
            //     // 开始遍历子View
            //     for (int i = 0; i < childrenCount; i++) {
            //         // add next child (in child order) to end of list
            //         // 是否设置有FLAG_USE_CHILD_DRAWING_ORDER
            //         // 如果设置了,绘制的方法就会通过调用getChildDrawingOrder()来决定绘制子View的顺序。
            //         // private int getAndVerifyPreorderedIndex(int childrenCount, int i, boolean customOrder) {
            //         //     final int childIndex;
            //         //     // 如果需要根据getChildDrawingOrder()排序子view的绘制顺序,就需要获得绘制顺序
            //         //     if (customOrder) {
            //         //         // 得到第i个View的绘制顺序,如果没有重写getChildDrawingOrder(),就会直接返回i
            //         //         // protected int getChildDrawingOrder(int childCount, int i) {
            //         //         //     return i;
            //         //         // }
            //         //         final int childIndex1 = getChildDrawingOrder(childrenCount, i);
            //         //         // 如果大于总数了,就抛异常
            //         //         if (childIndex1 >= childrenCount) {
            //         //             throw new IndexOutOfBoundsException("getChildDrawingOrder() "
            //         //                     + "returned invalid index " + childIndex1
            //         //                     + " (child count is " + childrenCount + ")");
            //         //         }
            //        //         childIndex = childIndex1;
            //         //     } else {
            //         //         // 否则直接返回遍历的下标
            //         //         childIndex = i;
            //         //     }
            //         //     return childIndex;
            //         // }
            //         final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
            //         // 获得这个View
            //         final View nextChild = mChildren[childIndex];
            //         final float currentZ = nextChild.getZ();
            //         // insert ahead of any Views with greater Z
            //         int insertIndex = i;
            //         // 向数组前面遍历查找是否有深度大于当前子View的
            //         while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
            //             insertIndex--;
            //         }
            //         // 将当前子View插入到这个位置,使集合按getZ()从小到大排序
            //         mPreSortedChildren.add(insertIndex, nextChild);
            //     }
            //     // 将排序好的集合返回
            //     return mPreSortedChildren;
            // }
            final ArrayList<View> preorderedList = buildTouchDispatchChildList();
            // true 完全根据排序方法的自定义实现,getZ()都不考虑
            final boolean customOrder = preorderedList == null
                    && isChildrenDrawingOrderEnabled();
            final View[] children = mChildren;
            // 从后往前
            for (int i = childrenCount - 1; i >= 0; i--) {
                // 获取设置了顺序绘制后对应第i个绘制的view的下标
                final int childIndex = getAndVerifyPreorderedIndex(
                        childrenCount, i, customOrder);
                // preorderedList不空就从它里面取出第childIndex个View
                // 空的话从children中取
                // 变量:几个child 是否设置了z 是否设置绘制顺序排序
                //   0   小于两个    都没设置        没有
                // 完全默认顺序
                // 0 0 0   childIndex默认     children的第i个
                // 0 1 0   childIndex默认     children的第i个
                // 0 1 0   childIndex默认     children的第i个
                // 1 0 0   childIndex默认     children的第i个
                // 完全绘制排序
                // 0 0 1   childIndex有顺序   children的第i对应的顺序个
                // 0 1 1   childIndex有顺序   children的第childIndex个
                // 1 0 1   childIndex有顺序   children的第childIndex个
                // 第一排序为getZ()的升序 第二排序会绘制顺序
                // 1 1 0   childIndex默认     preorderedList的第i个
                // 1 1 1   childIndex默认     preorderedList的第i个
                final View child = getAndVerifyPreorderedView(
                        preorderedList, children, childIndex);
                // If there is a view that has accessibility focus we want it
                // to get the event first and if not handled we will perform a
                // normal dispatch. We may do a double iteration but this is
                // safer given the timeframe.
                // 该viewgroup设置事件了指向焦点View并且焦点View在前面已经找到了
                if (childWithAccessibilityFocus != null) {
                    // 判断遍历的此View是否是焦点View,如果不是就直接下一遍循环
                    if (childWithAccessibilityFocus != child) {
                        continue;
                    }
                    // 如果是的话就将找到的焦点view置空
                    // i回到到数第一个下标
                    // 这样做的目的是先让该焦点view尝试进行下面的普通分发操作
                    // 如果成功了,会在下面跳出循环。
                    // 如果不成功,就将记录的焦点view置空,
                    // 从最后一个开始重新遍历,不再进入这个判断。
                    childWithAccessibilityFocus = null;
                    i = childrenCount - 1;
                }
                // private static boolean canViewReceivePointerEvents(@NonNull View child) {
                //     // 可见或者存在动画
                //     return (child.mViewFlags & VISIBILITY_MASK) == VISIBLE
                //             || child.getAnimation() != null;
                // }
                // isTransformedTouchPointInView()用来盘那段事件是否在view的范围内
                // protected boolean isTransformedTouchPointInView(float x, float y, View child,
                //         PointF outLocalPoint) {
                //     final float[] point = getTempPoint();
                //     point[0] = x;
                //     point[1] = y;
                //     // 把原来相对于该ViewGroup的坐标转换为相对于正在遍历的这个View的相对坐标
                //     // public void transformPointToViewLocal(float[] point, View child) {
                //     //     point[0] += mScrollX - child.mLeft;
                //     //     point[1] += mScrollY - child.mTop;
                //     //     if (!child.hasIdentityMatrix()) {
                //     //         child.getInverseMatrix().mapPoints(point);
                //     //     }
                //     // }
                //     transformPointToViewLocal(point, child);
                //     // /*package*/ final boolean pointInView(float localX, float localY) {
                //     //     return pointInView(localX, localY, 0);
                //     // }
                //     // public boolean pointInView(float localX, float localY, float slop) {
                //     //     return localX >= -slop && localY >= -slop && localX < ((mRight - mLeft) + slop) &&
                //     //             localY < ((mBottom - mTop) + slop);
                //     // }
                //     // 判断是否在view的范围内
                //     final boolean isInView = child.pointInView(point[0], point[1]);
                //     // 将转换后的坐标设置给outLocalPoint
                //     if (isInView && outLocalPoint != null) {
                //         outLocalPoint.set(point[0], point[1]);
                //     }
                //     // 返回是否在范围内
                //     return isInView;
                // }
                // 如果不可见并且没有动画 或者 事件不在该View范围内
                if (!canViewReceivePointerEvents(child)
                        || !isTransformedTouchPointInView(x, y, child, null)) {
                    // 设置false,直接跳出进入下一次循环
                    ev.setTargetAccessibilityFocus(false);
                    continue;
                }
                // 找到child是正在遍历的view的Touch Target
                newTouchTarget = getTouchTarget(child);
                // 如果存在,说明这个view之前处理过事件,再给它这个事件并跳出循环。
                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;
                }
                // 取消该view的PFLAG_CANCEL_NEXT_UP_EVENT
                resetCancelNextUpFlag(child);
                // 将事件传递给child看是否能分发消耗成功,如果成功就进方法块
                if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                    // Child wants to receive touch within its bounds.
                    mLastTouchDownTime = ev.getDownTime();
                    if (preorderedList != null) {
                        // childIndex points into presorted list, find original index
                        for (int j = 0; j < childrenCount; j++) {
                            if (children[childIndex] == mChildren[j]) {
                                // 找到该view的原始下标
                                mLastTouchDownIndex = j;
                                break;
                            }
                        }
                    } else {
                        // 如果集合为空,也就说明child是根据原始下标取出的,直接赋值
                        mLastTouchDownIndex = childIndex;
                    }
                    mLastTouchDownX = ev.getX();
                    mLastTouchDownY = ev.getY();
                    // 上面已经判断了Touch Target存在的情况
                    // 这里一定不存在,新创建一个对象并加入链表
                    // private TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
                    //     final TouchTarget target = TouchTarget.obtain(child, pointerIdBits);
                    //     target.next = mFirstTouchTarget;
                    //     mFirstTouchTarget = target;
                    //     return target;
                    // }
                    // 头插,现在mFirstTouchTarget就指向这个view的touch target了
                    newTouchTarget = addTouchTarget(child, idBitsToAssign);
                    // 置为true表示已经分发给新目标了
                    alreadyDispatchedToNewTouchTarget = true;
                    // 跳出遍历子View的循环
                    break;
                }
                // The accessibility focus didn't handle the event, so clear
                // the flag and do a normal dispatch to all children.
                // 焦点View没有成功处理事件所以将flag置为false,以后就普通的去遍历分发了
                ev.setTargetAccessibilityFocus(false);
            }
            // 清空排序集合
            if (preorderedList != null) preorderedList.clear();
        }
        // 只有当上面的if判断没有进入才会出现newTouchTarget为空的现象
        // 就说明该viewgroup的子view数量为0
        // 没有子view并且之前消耗过事件
        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;
            // 找到消耗链表的最后一个元素,也就是第一个被添加的元素,down事件消耗的view
            while (newTouchTarget.next != null) {
                newTouchTarget = newTouchTarget.next;
            }
            // 给这个目标加上这个事件的id
            newTouchTarget.pointerIdBits |= idBitsToAssign;
        }
    }
}

dispatchTouchEvent()框图

分发消耗框图

排序子View数组框图

上一篇 下一篇

猜你喜欢

热点阅读