ViewGroup拦截事件 子View为何触发子View CANCEL事件以及后续事件如何分发到父容器

类似的场景非常之多,例如,父容器需要水平滑动,子View需要垂直滑动等,一般我会重写父容器的onInterceptTouchEvent方法,如果手势水平移动,则父容器拦截事件,onInterceptTouchEvent方法返回true.

这里就分析上面的场景

  • Down下来时候, ViewGroup不拦截该事件,最终结果是mFirstTouchTarget不等于null了.
  • 当手势为上下滑动时,ViewGroup不拦截事件,并且事件会继续传到onInterceptTouchEvent中来,后续事件由dispatchTransformedTouchEvent分发到子View中

在这里插入图片描述
在这里插入图片描述

  • 当手势为左右滑动时,ViewGroup会拦截事件.
if (actionMasked == MotionEvent.ACTION_DOWN|| mFirstTouchTarget != null) {
    final boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
    if (!disallowIntercept) {
        //事件被拦截之后,intercepted = true
        intercepted = onInterceptTouchEvent(ev);
        ev.setAction(action); 
    } else {
        intercepted = false;
    }
} else {
    intercepted = true;
}

//事件被拦截之后,mFirstTouchTarget此时的值为子View,还不是null,直接走到else中.
if (mFirstTouchTarget == null) {
	...
} else {
    ...
    //因为 intercepted = true, 所以cancelChild为true.看看dispatchTransformedTouchEvent方法中,如果cancelChild为true时候会做什么.   
	final boolean cancelChild = resetCancelNextUpFlag(target.child)|| intercepted;
    if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {
    	handled = true;
    }
    ...                                                  
}
  • 子View为何会收到MotionEvent.ACTION_CANCEL事件
//看看cancel 为true时候会做什么.
private boolean dispatchTransformedTouchEvent(MotionEvent event, boolean cancel,
        View child, int desiredPointerIdBits) {
    final boolean handled;
    final int oldAction = event.getAction();
    //cancel为true
    if (cancel || oldAction == MotionEvent.ACTION_CANCEL) {
        //会将该事件设置为MotionEvent.ACTION_CANCEL
        event.setAction(MotionEvent.ACTION_CANCEL);
        if (child == null) {
            //再将该事件分发到子View中, 子View就会收到MotionEvent.ACTION_CANCEL事件
            handled = super.dispatchTouchEvent(event);
        } else {
            handled = child.dispatchTouchEvent(event);
        }
        event.setAction(oldAction);
        //子View会返回true,消耗掉该事件.
        return handled;
    }
}    
  • 事件如何分发到ViewGroup中
//在ViewGroup中,dispatchTouchEvent方法中最后执行的几行代码会将mFirstTouchTarget置为null
//如此,当非Down事件进来之后会直分发到ViewGroup中.
public boolean dispatchTouchEvent(MotionEvent ev) {
	...
    //若mFirstTouchTarget等于null,会执行dispatchTransformedTouchEvent方法,参数View 为null.
    //这样事件就被分发到    
	if (mFirstTouchTarget == null) {
    handled = dispatchTransformedTouchEvent(ev, canceled, null,TouchTarget.ALL_POINTER_IDS);
	} else {
        TouchTarget predecessor = null;
        TouchTarget target = mFirstTouchTarget;
        final TouchTarget next = target.next;
        // 这里就表示为Down以外的事件分发流程了.
        // intercepted表示ViewGroup是否拦截该次事件.
        // 拦截时intercepted=true则cancelChild=true,不拦截时intercepted=false则cancelChild=false.
        final boolean cancelChild = resetCancelNextUpFlag(target.child) || intercepted;
        // 这里是将事件分发到目标子View中,dispatchTransformedTouchEvent()下面分析.
        if (dispatchTransformedTouchEvent(ev, cancelChild,target.child, target.pointerIdBits)) {
            // 如果目标子View消耗了该事件,ViewGroup.dispatchTouchEvent()就返回true.
            handled = true;
        }
        // 如果ViewGruop拦截事件,则cancelChild=true.
        if (cancelChild) {
            // 这句话意思是mFirstTouchTarget将被他的上一个TouchTarget取代.TouchTarget是一个链表结构.
            // mFirstTouchTarget对象中的当前目标子View对象,今后将不会在接收到当前的事件序列中的任何事件了.
            // 假如当前mFirstTouchTarget上一个TouchTarget对象为null,那么mFirstTouchTarget也将为null,那么当该事件序列中的下一个事件到来时,ViewGroup将会把该事件交由自己处理.
            mFirstTouchTarget = next;
        }
	}
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值