Android OnInterceptTouchEvent、dispatchTouchEvent、onTouchEvent时序逻辑分析结论

本文将讨论嵌套布局,子view重叠时,当点击子view,事件的分发过程,主要针对自定义viewGroup和自定义View:


     要知道viewGroup和view所关心的事件,前者是 OnInterceptTouchEvent、dispatchTouchEvent、onTouchEvent,而后者没有onInterceptTouchEvent方法,即前者可以进行

事件的拦截。一旦在上级中拦截了Touch事件,则后面根本不会触发任何方法。

dispatchTouchEvent()进行事件的分发,只要事件没有被拦截,那么该方法一定会被调用,返回结果表示是否消耗事件。

onTouchEvent在dispatchTouchEvent中调用,如果返回false,则表明该控件未接收事件,true则表示消耗该事件。


假设本文的布局背景为,两个自定义viewGroup中嵌套一个自定义view控件。<ViewGroup2>   <ViewGroup1><View/></ViewGroup1>   </ViewGroup2>,从整个事件的触发流程

来看,当一个touch行为产生时,通过方法里的log,来判断事件的触发情况。

首先,如果ViewGroup和View中方法均返回false,即对touch事件不拦截,正常分发。

                                                                

如果在ViewGroup1的disPatchTouchEvent()返回True,则情况如下图,说明ViewGroup1消耗了事件,不会再向下分发,也不会处理。

                                                                


如果在返回false,则情况如下图,说明在本层不再继续分发,并交给上层的onTouchEvent事件处理。

                                                                

                                                        


下载Android源码, 在Frameworks/base/core/java/android/view/ViewGroup.java中有具体实现。

disPatchTouchEvent的源码实现过程:首先判断actionMasked,如果是DOWN,就会默认为起始操作,重置TouchTarget,TouchState等参数。接着用if (actionMasked 

== MotionEvent.ACTION_DOWN || mFirstTouchTarget !=null) , 调用了Intercept方法是判断如果是起始动作才拦截,或者已经有人消费掉了事件,再去判断拦截,起始动作是第一次向下分

发的时候,每个view都可以决定是否拦截,然后进一步判断是否消费,如果有人消费掉了事件,那么也拦截,不再向下传递。

在判断是否拦截后,他才会开始分发操作:

if (child == null) {
        handled = super.dispatchTouchEvent(transformedEvent);
    } else {
        handled = child.dispatchTouchEvent(transformedEvent);
    }

简化来讲,如果自身没有child,(自身是VIewGroup且没child或自身为view)就会调用父类的disPatchTouchEvent方法即view的方法,而该方法如下代码:

if (li != null && li.mOnTouchListener != null
   && (mViewFlags & ENABLED_MASK) == ENABLED
             && li.mOnTouchListener.onTouch(this, event)) {
         result = true;
       }
       if (!result && onTouchEvent(event)) {
             result = true;
           }
这里实际就是调用了onTouchEvent,并返回其值。

如果自身有child,就会继续向下分发至子控件去,并由子控件继续分发。

因此,disPatchTouchEvent实际上最终执行的还是onTouchEvent。


接下来讨论onInerceptTouchEvent(),该方法就是把事件拦截下来,和disPatchTouchEvent不同的是,他不会消耗事件,只会留给自己以及上层架构使用,比如在

ViewGroup1的onInerceptTouchEvent()事件返回true,则情况如下图:

                                             

从源码来看,该方法里仅做了个if判断,若要实现我们想要的效果,可以重写过滤条件。(个人感觉。。)


最后,来分析onTouchEvent()方法,返回true相当于onTouchEvent方法把事件已经处理掉了,就不存在继续向上层传递的情况,表现为由子view逐层向外处理

onTouchEvent时,如果某一层的onTouchEvent事件返回true,则后面的onTouch事件将不再触发,这里就不再贴图演示。


最终分析整个Touch事件的流程,我们可以把ViewGroup2的dispatchTouchEvent当成方法的一个主入口,它可能会触发后面所有控件的方法。具体流程是:当Touch事件

发生时,调用最外层disPatchTouchEvent(此方法是一个过程,到判断拦截也没有结束),此时,该方法会先判断是否拦截,即调用onInterceptTouchEvent(),若未拦截,

则由于disPatchTouchEvent的返回值由它的子view决定,就会去调用其子ViewGroup1的disPatchTouchEvent,它也会走判断拦截的过程。当到达最终控件时,不存在子

view,或它自身就是一个view,就会直接用onTouchEvent方法,开始判断是否消耗,从而返回结果给上级的disPatchTouchEvent,因此,会逐级调用onTouchEvent。

整体流程图如下:

                                                   (该图引用自单灿灿博客,很准确,不再作图)


此时,如果使用getaction函数,会发现,在上述的几种场景中,返回不同的行为,可能为UP,Down,Move等行为,如果注意到行为变化的时机时,就会发现,仅有事件

被消耗时,才会有UP的动作判断,这个其实很好判断,如果连DOWN事件都没处理,怎么会有抬手的动作,Move等动作。

Up,Move等事件,其传递和判断流程和up完全一致,只需要注意他们的发生是有前提条件的。比如在ViewGroup1处理了OntouchEvent事件,并返回true,log如下:  

图中,onTouchEvent到ViewGroup1结束,接下来就开始其他动作的判断,且由于子view没处理Down的onTouch事件,所以,move动作只在ViewGroup2和ViewGroup1之间

传递。这里要注意一点,第一次Down的时候会for循环所有child,但在源码中,dispatchTouchEvent其实每次使用了if(mFirstTouchTarget==NULL)在判断是否有收到Touch事

件。如果像上面一样,进入了Down之后的事件,就会进行一个while循环,在其中进行Touchtarget next=target.next,这个next是遍历同级,而不是子集,在代码里可以看到

newTouchTarget=addTouchTarget(child,idBitsToAssign)。这个对象保存了传递路线信息,是一个链式结构不过这个路线不是ViewGroup2->ViewGroup1->View的一个

单子,而是每个ViewGroup都会保存一个向下的路线信息。会再次调用dispatchTransformedTouchEvent来处理后续的动作。
























































































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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值