android 事件分发源码分析

发布于:2025-08-29 ⋅ 阅读:(19) ⋅ 点赞:(0)

1)DOWN事件触发时重置标志位;

2)判断是否拦截事件(根据FLAG_DISALLOW_INTERCEPT标志和onInterceptTouchEvent返回值);

3)遍历子View寻找能处理事件的View,通过dispatchTransformedTouchEvent询问子View;

4)将处理事件的子View保存到mFirstTouchTarget链表;

5)后续事件根据mFirstTouchTarget和拦截状态决定分发路径。当父容器拦截时,会向子View发送CANCEL事件并清空mFirstTouchTarget。整个过程实现了从父容器到子View的事件分发与拦截机制

  • 置空标志位置
   // 在DOWN事件的时候会置空标志位置
        if (actionMasked == MotionEvent.ACTION_DOWN) {
            // 清掉旧 TouchTarget,清空mFirstTouchTarget
            cancelAndClearTouchTargets(ev);
            // 重置 FLAG_DISALLOW_INTERCEPT 等
            resetTouchState();
        }

        private void cancelAndClearTouchTargets(MotionEvent event) {
            //清空mFirstTouchTarget
            clearTouchTargets();
        }
        //清空mFirstTouchTarget
        private void clearTouchTargets() {
            ViewGroup.TouchTarget target = mFirstTouchTarget;
            if (target != null) {
                do {
                    ViewGroup.TouchTarget next = target.next;
                    target.recycle();
                    target = next;
                } while (target != null);
                mFirstTouchTarget = null;
            }
        }
  • 第一块代码,判断事件是否由父容器拦截
  //是否拦截标志
        final boolean intercepted;
        //判断是否是DOWN事件,或者是mFirstTouchTarget第一个处理事件的View
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            //requestDisallowInterceptTouchEvent决定这里的属性,true这里为true,false这里为false
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            if (!disallowIntercept) {
                //父容器是否拦截事件
                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;
        }
  • 第二块代码,决定是由父容器还是子View处理事件
     ViewGroup.TouchTarget newTouchTarget = null;
        boolean alreadyDispatchedToNewTouchTarget = false;
        //判断是否是拦截事件
        if (!canceled && !intercepted) {
            //单手指DOWN,多手指ACTION_POINTER_DOWN,鼠标DOWN ACTION_HOVER_MOVE
            if (actionMasked == MotionEvent.ACTION_DOWN
                    || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN)
                    || actionMasked == MotionEvent.ACTION_HOVER_MOVE) {

                //这里newTouchTarget=null,判断是否存在子View
                if (newTouchTarget == null && childrenCount != 0) {
                    final float x =
                            isMouseEvent ? ev.getXCursorPosition() : ev.getX(actionIndex);
                    final float y =
                            isMouseEvent ? ev.getYCursorPosition() : ev.getY(actionIndex);
                    // Find a child that can receive the event.
                    // Scan children from front to back.
                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    final View[] children = mChildren;
                    //开始遍历子View
                    for (int i = childrenCount - 1; i >= 0; i--) {
                        //确定该点位落在那个View
                        if (!child.canReceivePointerEvents()
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }

                        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);
                        //找到落点位置的View,开始询问View是否处理事件
                        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]) {
                                        mLastTouchDownIndex = j;
                                        break;
                                    }
                                }
                            } else {
                                mLastTouchDownIndex = childIndex;
                            }
                            mLastTouchDownX = ev.getX();
                            mLastTouchDownY = ev.getY();
                            //有View处理事件,开始修改标志位置,将mFirstTouchTarget设置为当前处理的View,newTouchTarget = mFirstTouchTarget
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            //表示down事件已经有View处理
                            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();
                }

            }

            //标准位进行赋值,mFirstTouchTarget = target, target.next = null,mFirstTouchTarget.next = null
            private ViewGroup.TouchTarget addTouchTarget(@NonNull View child, int pointerIdBits) {
                final ViewGroup.TouchTarget target = ViewGroup.TouchTarget.obtain(child, pointerIdBits);
                //这里mFirstTouchTarget在DOWN的时候置空了,这里为空
                target.next = mFirstTouchTarget;
                //mFirstTouchTarget赋值为当前处理事件的View
                mFirstTouchTarget = target;
                return target;
            }

        }

       dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)

        private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
            // Perform any necessary transformations and dispatch.
            if (child == null) {
                handled = super.dispatchTouchEvent(transformedEvent);
            } else {
                //询问子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);
            }
        }
  • 第三块代码,对其他事件继续处理
 // Check for cancelation.
        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;
        //没有子View处理事件,将事件交个父容器,询问父容器是否需要处理事件
        if (mFirstTouchTarget == null) {
            //注释1:canceled = false
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    ViewGroup.TouchTarget.ALL_POINTER_IDS);
        } else {

            ViewGroup.TouchTarget predecessor = null;
            ViewGroup.TouchTarget target = mFirstTouchTarget;
            //mFirstTouchTarget = target, target.next = null,mFirstTouchTarget.next = null
            //这里不为空,执行该方法
            while (target != null) {
                //next 未空,下一次将不执行
                final ViewGroup.TouchTarget next = target.next;
                //DOWN alreadyDispatchedToNewTouchTarget = true 有View处理事件 alreadyDispatchedToNewTouchTarget = true,newTouchTarget = target = mFirstTouchTarget
                if (alreadyDispatchedToNewTouchTarget && target == newTouchTarget) {
                    handled = true;
                } else {
                    //DOWN事件没有处理获取其他事件 alreadyDispatchedToNewTouchTarget = false
                    final boolean cancelChild = resetCancelNextUpFlag(target.child)
                            || intercepted;
                    //注释2:对已经处理的事件进行分发
                    if (dispatchTransformedTouchEvent(ev, cancelChild,
                            target.child, target.pointerIdBits)) {
                        handled = true;
                    }
                    //当MOVE的时候,父容器拦截事件,将事件置为CANCEL事件后,将mFirstTouchTarget置空
                        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);
        }

        //注释1:
        //canceled = false
        handled = dispatchTransformedTouchEvent(ev, canceled, null,
                ViewGroup.TouchTarget.ALL_POINTER_IDS);
        private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
            //child = null,询问父容器是否处理事件
            if (child == null) {
                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());
                }

                handled = child.dispatchTouchEvent(transformedEvent);
            }
        }
        
        //注释2:
        final boolean cancelChild = resetCancelNextUpFlag(target.child)
                || intercepted;
        dispatchTransformedTouchEvent(ev, cancelChild,
                target.child, target.pointerIdBits)


        cancelChild =false;
        private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {

            if (child == null) {
                handled = super.dispatchTouchEvent(transformedEvent);
            } else {
                //询问子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);
            }
        }

        cancelChild = true;
        private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {

            //父类View拦截了事件,将事件改成cancel下发给View
            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;
            }
        }
  • 事件分发场景1

DOWN事件,

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View dispatchTouchEvent返回false,

条件3:父容器onTouch返回false,

  • 事件分发2

DOWN事件,

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View dispatchTouchEvent返回true,

条件3:父容器onTouch返回false,

  • 事件分发3

DOWN事件,

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View dispatchTouchEvent返回false,

条件3:父容器onTouch返回true,

  • 事件分发4

DOWN事件,

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View dispatchTouchEvent返回true,

条件3:父容器onTouch返回true,

同事件分发2,会首先响应子View的事件处理,响应后不再响应父容器的事件响应

  • 事件分发5

如果DOWN事件是父容器拦截了,那么子View将不会再收到事件

DOWN事件,

条件1:父容器onInterceptTouchEvent返回true,

条件2:子View dispatchTouchEvent返回true或者false,

条件3:父容器onTouch返回true或者false,

  • 事件分发6

条件1:父容器onInterceptTouchEvent返回false,

条件2:子View requestDisallowInterceptTouchEvent设置成true

条件3:子View dispatchTouchEvent返回true

条件4:父容器onTouch返回true或者false,

MOVE事件1,

条件1:父容器onInterceptTouchEvent返回true,

条件2:子View requestDisallowInterceptTouchEvent设置成false

条件3:子View dispatchTouchEvent返回true

条件4:父容器onTouch返回true,

MOVE事件2,

条件1:父容器onInterceptTouchEvent返回true,

条件2:requestDisallowInterceptTouchEvent设置成false

条件3:子View dispatchTouchEvent返回true

条件4:父容器onTouch返回true,

MOVE事件3,

条件1:父容器onInterceptTouchEvent返回true,

条件2:requestDisallowInterceptTouchEvent设置成false

条件3:子View dispatchTouchEvent返回true

条件4:父容器onTouch返回true,

子View只要在DOWN事件返回true就可以接收其他所有事件

在DOWN事件返回true的时候,会把响应的View保存到派发链表mFirstTouchTarget中,接下来的事件,只要父类不拦截,都会派发事件给当前mFirstTouchTarget保存的View,如果父容器有拦截,就会传递CANCEL事件给mFirstTouchTarget保存的View,并且把mFirstTouchTarget置空,以后的所有事件都会传给父容器


网站公告

今日签到

点亮在社区的每一天
去签到