Android的分发机制

  • 仔细看的话,图分为3层,从上往下依次是Activity、ViewGroup、View

    • 事件从左上角那个白色箭头开始,由Activity的dispatchTouchEvent做分发

    • 箭头的上面字代表方法返回值,(return true、return false、return super.xxxxx(),super 的意思是调用父类实现。

    • dispatchTouchEvent和 onTouchEvent的框里有个【true---->消费】的字,表示的意思是如果方法返回true,那么代表事件就此消费,不会继续往别的地方传了,事件终止。

    • 目前所有的图的事件是针对ACTION_DOWN的,对于ACTION_MOVE和ACTION_UP我们最后做分析。

    • 之前图中的Activity 的dispatchTouchEvent 有误(图已修复),只有return super.dispatchTouchEvent(ev) 才是往下走,返回true 或者 false 事件就被消费了(终止传递)。

  • Android的touch事件分发的方向是从父控件到子控件,而事件消费方向则是从子控件到父控件

一、父View拦截事件后,会向子View发送一个MotionEvent.ACTION_CANCEL

  • 父View拦截事件后,会将intercepted设置为true

  • 事件被拦截后,mFirstTouchTarget此时的值为子View,还不是null

  • 将该事件设置为MotionEvent.ACTION_CANCEL,将该事件分发到子View中

  • 子view会返回true消耗掉该事件

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

//看看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;
        }
    }

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值