ViewGroup事件分发机制

小结

每次按下直到取消或者抬起,都会进行一次事件分发。事件分发是由Activity传递给Window,Window传递给DecorView实际就是ViewGroup的dispatchTouchEvent这个方法进行分发。

  1. 如果ViewGroup的dispatchTouchEvent方法回false,那么只会响应ACTION_DOWN的事件,其它事件不再响应。
  2. 如果ViewGroup的dispatchTouchEvent方法回true,后续的其它事件还能继续分发到该View,也就是能继续响应其它事件。

ViewGroup的dispatchTouchEvent源码

@Override
public boolean dispatchTouchEvent(MotionEvent ev) {

	... ...
	
    // 是否进行过操作的记录
    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) {
            // 清空TouchTargets
            cancelAndClearTouchTargets(ev);
            // 重新设置Touch的状态
            resetTouchState();
            // 经过这两个方法 mFirstTouchTarget会被置为空
        }

        // 判断是否对事件进行拦截
        final boolean intercepted;
        //如果是按下事件 或者 没有子View在消费事件
        if (actionMasked == MotionEvent.ACTION_DOWN
                || mFirstTouchTarget != null) {
            // 子View是否充许事件拦截,默认为false 即调用requestDisallowIntercept()方法,设置true的话告诉父View不要对此次事件序列进行拦截
            final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
            //子View允许时间拦截,即disallowIntercept为false
            if (!disallowIntercept) {
                // 调用ViewGroup的onInterceptTouchEvent方法的拦截方式
                intercepted = onInterceptTouchEvent(ev);
                ev.setAction(action); // restore action in case it was changed
            } else {
                intercepted = false;
            }
        } else {
        	//没有子View处理事件并且不是初始化的Down事件,就对事件进行拦截
            intercepted = true;
        }

       
        // 检查事件有没有取消
        final boolean canceled = resetCancelNextUpFlag(this)
                || actionMasked == MotionEvent.ACTION_CANCEL;

        // Update list of touch targets for pointer down, if needed.
        final boolean split = (mGroupFlags & FLAG_SPLIT_MOTION_EVENTS) != 0;
        TouchTarget newTouchTarget = null;
       
        boolean alreadyDispatchedToNewTouchTarget = false;
        //如果不拦截并且不是canceled
        if (!canceled && !intercepted) {

			//如果是一个down事件 或者 split(多点触控的参数)或者
            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
                final int idBitsToAssign = split ? 1 << ev.getPointerId(actionIndex)
                        : TouchTarget.ALL_POINTER_IDS;

               

                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.
                    final ArrayList<View> preorderedList = buildTouchDispatchChildList();
                    final boolean customOrder = preorderedList == null
                            && isChildrenDrawingOrderEnabled();
                    // 循环子View
                    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);
                        
                        // isTransformedTouchPointInView(x, y, child, null)判断事件是不是在这个子View上
                        if (!canViewReceivePointerEvents(child)
                                || !isTransformedTouchPointInView(x, y, child, null)) {
                            ev.setTargetAccessibilityFocus(false);
                            continue;
                        }                           
                        
                        // 分发事件的重要方法dispatchTransformedTouchEvent
                        if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
                           
                            // addTouchTarget这个方法给mFirstTouchTarget赋值
                            newTouchTarget = addTouchTarget(child, idBitsToAssign);
                            alreadyDispatchedToNewTouchTarget = true;
                            break;
                        }                         
                    }
                    
                }
            }
        }

        // 如果没有子View消费掉事件,就自己消费掉
        if (mFirstTouchTarget == null) {
            // 这里最终执行View的事件分发
            handled = dispatchTransformedTouchEvent(ev, canceled, null,
                    TouchTarget.ALL_POINTER_IDS);
        } else {
            // TouchTarget是手指和被摸到的子View的绑定,是一个链表结构
            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 (predecessor == null) {
                            mFirstTouchTarget = next;
                        } else {
                            predecessor.next = next;
                        }
                        target.recycle();
                        target = next;
                        continue;
                    }
                }
                predecessor = target;
                target = next;
            }
        }  
    }

    return handled;
}

   

true:子View请求当前ViewGroup不要拦截此次事件序列

    @Override
    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {

        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
            // We're already in this state, assume our ancestors are too
            return;
        }

        if (disallowIntercept) {
            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
        } else {
            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
        }

        // Pass it up to our parent
        if (mParent != null) {
            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
        }
    }

处理事件分发的重要方法

private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    final boolean handled;

    // 删掉了N行代码...
    if (child == null) {
        // 去调用自己父类View的事件分发
        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());
        }
        // 调用子View的事件分发 
        handled = child.dispatchTouchEvent(transformedEvent);
    }

    // Done.
    transformedEvent.recycle();
    return handled;
}
  • 正常的一次分发:
    ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvnet -> View.dispatchTouchEvent -> View.onTouchEvent -> ViewGroup.onTouchEvent

  • ViewGroup拦截了事件
    也就是ViewGroup.onIntercepterTouchEvent 返回ture:
    ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvent -> ViewGroup.onTouchEvent

  • ViewGroup.onTouchEvent返回true,其它地方返回false:
    **ACTION_DOWN:**ViewGroup.dispatchTouchEvent -> ViewGroup.onIntercepterTouchEvnet -> View.dispatchTouchEvent -> View.onTouchEvent -> ViewGroup.onTouchEvent
    **ACTION_MOVE:**ViewGroup.dispatchTouchEvent -> ViewGroup.onTouchEvent
    **ACTION_UP:**ViewGroup.dispatchTouchEvent -> ViewGroup.onTouchEvent

MOVE和UP事件,因为mFirstTarget为空,onIntercepterTouchEvnet 这个方法执行不了,子类的事件执行不了。

分发事件总结

  1. 如果说子 View 没有一个地方返回 true ,只会进来一次只会响应 DOWN 事件,代表不需要消费该事件,如果你想响应 后续事件如move、up等,必须找个地方ture。
  2. 对于ViewGroup来讲,如果你想拦截子 View 的 Touch 事件,可以覆写 onInterceptTouchEvent ,在里面进行相应的逻辑处理,返回 true 表示拦截 , 然后执行该 ViewGroup 的 onTouchEvent 方法 , 如果子 View 没有消费 touch 事件也会调用该 ViewGroup 的 onTouchEvent 方法
发布了206 篇原创文章 · 获赞 68 · 访问量 6万+

猜你喜欢

转载自blog.csdn.net/u013728021/article/details/102831095
今日推荐