美文网首页ITBOX知识大全安卓
图解 Android 事件分发机制

图解 Android 事件分发机制

作者: Kelin | 来源:发表于2016-06-20 18:07 被阅读125489次

    在Android开发中,事件分发机制是一块Android比较重要的知识体系,了解并熟悉整套的分发机制有助于更好的分析各种点击滑动失效问题,更好去扩展控件的事件功能和开发自定义控件,同时事件分发机制也是Android面试必问考点之一,如果你能把下面的一些事件分发图当场画出来肯定加分不少。废话不多说,总结一句:事件分发机制很重要

    Android 事件分发流###

    关于Android 事件分发机制网上的博文很多,但是很多都是写个Demo然后贴一下输出的Log或者拿源码分析,然后一堆的注释和说明,如果用心的去看肯定是收获不少但是确实很难把整个流程说清和记住。曾经也是拼命想记住整个流程,但是一段时间又忘了,最后觉得分析这种问题和事件流的走向,一张图来解释和说明会清晰很多,下面我根据画的一张事件分发流程图,说明的事件从用户点击之后,在不同函数不同返回值的情况的最终走向。

    图 1.

    注:

    • 仔细看的话,图分为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 事件就被消费了(终止传递)。

    仔细看整个图,我们得出事件流 走向的几个结论(希望读者专心的看下图 1,多看几遍,脑子有比较清晰的概念。)
    1、如果事件不被中断,整个事件流向是一个类U型图,我们来看下这张图,可能更能理解U型图的意思。

    图 2.

    所以如果我们没有对控件里面的方法进行重写或更改返回值,而直接用super调用父类的默认实现,那么整个事件流向应该是从Activity---->ViewGroup--->View 从上往下调用dispatchTouchEvent方法,一直到叶子节点(View)的时候,再由View--->ViewGroup--->Activity从下往上调用onTouchEvent方法。

    2、dispatchTouchEvent 和 onTouchEvent 一旦return true,事件就停止传递了(到达终点)(没有谁能再收到这个事件)。看下图中只要return true事件就没再继续传下去了,对于return true我们经常说事件被消费了,消费了的意思就是事件走到这里就是终点,不会往下传,没有谁能再收到这个事件了

    图 3.
    3、dispatchTouchEvent 和 onTouchEvent return false的时候事件都回传给父控件的onTouchEvent处理。 图 4.

    看上图深蓝色的线,对于返回false的情况,事件都是传给父控件onTouchEvent处理。

    • 对于dispatchTouchEvent 返回 false 的含义应该是:事件停止往子View传递和分发同时开始往父控件回溯(父控件的onTouchEvent开始从下往上回传直到某个onTouchEvent return true),事件分发机制就像递归,return false 的意义就是递归停止然后开始回溯。
    • 对于onTouchEvent return false 就比较简单了,它就是不消费事件,并让事件继续往父控件的方向从下往上流动。

    4、dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent
    ViewGroup 和View的这些方法的默认实现就是会让整个事件安装U型完整走完,所以 return super.xxxxxx() 就会让事件依照U型的方向的完整走完整个事件流动路径),中间不做任何改动,不回溯、不终止,每个环节都走到。

    Paste_Image.png

    所以如果看到方法return super.xxxxx() 那么事件的下一个流向就是走U型下一个目标,稍微记住上面这张图,你就能很快判断出下一个走向是哪个控件的哪个函数。
    5、onInterceptTouchEvent 的作用


    图 5.

    Intercept 的意思就拦截,每个ViewGroup每次在做分发的时候,问一问拦截器要不要拦截(也就是问问自己这个事件要不要自己来处理)如果要自己处理那就在onInterceptTouchEvent方法中 return true就会交给自己的onTouchEvent的处理,如果不拦截就是继续往子控件往下传。默认是不会去拦截的,因为子View也需要这个事件,所以onInterceptTouchEvent拦截器return super.onInterceptTouchEvent()和return false是一样的,是不会拦截的,事件会继续往子View的dispatchTouchEvent传递

    6、ViewGroup 和View 的dispatchTouchEvent方法返回super.dispatchTouchEvent()的时候事件流走向。


    图 6

    首先看下ViewGroup 的dispatchTouchEvent,之前说的return true是终结传递。return false 是回溯到父View的onTouchEvent,然后ViewGroup怎样通过dispatchTouchEvent方法能把事件分发到自己的onTouchEvent处理呢,return true和false 都不行,那么只能通过Interceptor把事件拦截下来给自己的onTouchEvent,所以ViewGroup dispatchTouchEvent方法的super默认实现就是去调用onInterceptTouchEvent,记住这一点
    那么对于View的dispatchTouchEvent return super.dispatchTouchEvent()的时候呢事件会传到哪里呢,很遗憾View没有拦截器。但是同样的道理return true是终结。return false 是回溯会父类的onTouchEvent,怎样把事件分发给自己的onTouchEvent 处理呢,那只能return super.dispatchTouchEvent,View类的dispatchTouchEvent()方法默认实现就是能帮你调用View自己的onTouchEvent方法的。

    说了这么多,不知道有说清楚没有,我这边最后总结一下:

    • 对于 dispatchTouchEvent,onTouchEvent,return true是终结事件传递。return false 是回溯到父View的onTouchEvent方法。
    • ViewGroup 想把自己分发给自己的onTouchEvent,需要拦截器onInterceptTouchEvent方法return true 把事件拦截下来。
    • ViewGroup 的拦截器onInterceptTouchEvent 默认是不拦截的,所以return super.onInterceptTouchEvent()=return false;
    • View 没有拦截器,为了让View可以把事件分发给自己的onTouchEvent,View的dispatchTouchEvent默认实现(super)就是把事件分发给自己的onTouchEvent。

    ViewGroup和View 的dispatchTouchEvent 是做事件分发,那么这个事件可能分发出去的四个目标

    注:------> 后面代表事件目标需要怎么做。
    1、 自己消费,终结传递。------->return true ;
    2、 给自己的onTouchEvent处理-------> 调用super.dispatchTouchEvent()系统默认会去调用 onInterceptTouchEvent,在onInterceptTouchEvent return true就会去把事件分给自己的onTouchEvent处理。
    3、 传给子View------>调用super.dispatchTouchEvent()默认实现会去调用 onInterceptTouchEvent 在onInterceptTouchEvent return false,就会把事件传给子类。
    4、 不传给子View,事件终止往下传递,事件开始回溯,从父View的onTouchEvent开始事件从下到上回归执行每个控件的onTouchEvent------->return false;
    注: 由于View没有子View所以不需要onInterceptTouchEvent 来控件是否把事件传递给子View还是拦截,所以View的事件分发调用super.dispatchTouchEvent()的时候默认把事件传给自己的onTouchEvent处理(相当于拦截),对比ViewGroup的dispatchTouchEvent 事件分发,View的事件分发没有上面提到的4个目标的第3点。

    ViewGroup和View的onTouchEvent方法是做事件处理的,那么这个事件只能有两个处理方式:

    1、自己消费掉,事件终结,不再传给谁----->return true;
    2、继续从下往上传,不消费事件,让父View也能收到到这个事件----->return false;View的默认实现是不消费的。所以super==false。

    ViewGroup的onInterceptTouchEvent方法对于事件有两种情况:

    1、拦截下来,给自己的onTouchEvent处理--->return true;
    2、不拦截,把事件往下传给子View---->return false,ViewGroup默认是不拦截的,所以super==false;

    关于ACTION_MOVE 和 ACTION_UP####

    上面讲解的都是针对ACTION_DOWN的事件传递,ACTION_MOVE和ACTION_UP在传递的过程中并不是和ACTION_DOWN 一样,你在执行ACTION_DOWN的时候返回了false,后面一系列其它的action就不会再得到执行了。简单的说,就是当dispatchTouchEvent在进行事件分发的时候,只有前一个事件(如ACTION_DOWN)返回true,才会收到ACTION_MOVE和ACTION_UP的事件。具体这句话很多博客都说了,但是具体含义是什么呢?我们来看一下下面的具体分析。

    上面提到过了,事件如果不被打断的话是会不断往下传到叶子层(View),然后又不断回传到Activity,dispatchTouchEvent 和 onTouchEvent 可以通过return true 消费事件,终结事件传递,而onInterceptTouchEvent 并不能消费事件,它相当于是一个分叉口起到分流导流的作用,ACTION_MOVE和ACTION_UP 会在哪些函数被调用,之前说了并不是哪个函数收到了ACTION_DOWN,就会收到 ACTION_MOVE 等后续的事件的。
    下面通过几张图看看不同场景下,ACTION_MOVE事件和ACTION_UP事件的具体走向并总结一下规律。

    1、我们在ViewGroup1 的dispatchTouchEvent 方法返回true消费这次事件

    ACTION_DOWN 事件从(Activity的dispatchTouchEvent)--------> (ViewGroup1 的dispatchTouchEvent) 后结束传递,事件被消费(如下图红色的箭头代码ACTION_DOWN 事件的流向)。

    //打印日志
    Activity | dispatchTouchEvent --> ACTION_DOWN 
    ViewGroup1 | dispatchTouchEvent --> ACTION_DOWN
    ---->消费
    

    在这种场景下ACTION_MOVE和ACTION_UP 将如何呢,看下面的打出来的日志

    Activity | dispatchTouchEvent --> ACTION_MOVE 
    ViewGroup1 | dispatchTouchEvent --> ACTION_MOVE
    ----
    TouchEventActivity | dispatchTouchEvent --> ACTION_UP 
    ViewGroup1 | dispatchTouchEvent --> ACTION_UP
    ----
    

    下图中
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向


    2、我们在ViewGroup2 的dispatchTouchEvent 返回true消费这次事件

    Activity | dispatchTouchEvent --> ACTION_DOWN 
    ViewGroup1 | dispatchTouchEvent --> ACTION_DOWN
    ViewGroup1 | onInterceptTouchEvent --> ACTION_DOWN
    ViewGroup2 | dispatchTouchEvent --> ACTION_DOWN
    ---->消费
    Activity | dispatchTouchEvent --> ACTION_MOVE 
    ViewGroup1 | dispatchTouchEvent --> ACTION_MOVE
    ViewGroup1 | onInterceptTouchEvent --> ACTION_MOVE
    ViewGroup2 | dispatchTouchEvent --> ACTION_MOVE
    ----
    TouchEventActivity | dispatchTouchEvent --> ACTION_UP 
    ViewGroup1 | dispatchTouchEvent --> ACTION_UP
    ViewGroup1 | onInterceptTouchEvent --> ACTION_UP
    ViewGroup2 | dispatchTouchEvent --> ACTION_UP
    ----
    

    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向


    Paste_Image.png

    3、我们在View 的dispatchTouchEvent 返回true消费这次事件
    这个我不就画图了,效果和在ViewGroup2 的dispatchTouchEvent return true的差不多,同样的收到ACTION_DOWN 的dispatchTouchEvent函数都能收到 ACTION_MOVE和ACTION_UP。
    所以我们就基本可以得出结论如果在某个控件的dispatchTouchEvent 返回true消费终结事件,那么收到ACTION_DOWN 的函数也能收到 ACTION_MOVE和ACTION_UP。

    4、我们在View 的onTouchEvent 返回true消费这次事件
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向

    5、我们在ViewGroup 2 的onTouchEvent 返回true消费这次事件
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向


    6、我们在ViewGroup 1 的onTouchEvent 返回true消费这次事件
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向

    7、我们在Activity 的onTouchEvent 返回true消费这次事件
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向

    8、我们在View的dispatchTouchEvent 返回false并且Activity 的onTouchEvent 返回true消费这次事件
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向

    9、我们在View的dispatchTouchEvent 返回false并且ViewGroup 1 的onTouchEvent 返回true消费这次事件
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向

    10、我们在View的dispatchTouchEvent 返回false并且在ViewGroup 2 的onTouchEvent 返回true消费这次事件
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向

    11、我们在ViewGroup2的dispatchTouchEvent 返回false并且在ViewGroup1 的onTouchEvent返回true消费这次事件
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向

    12、我们在ViewGroup2的onInterceptTouchEvent 返回true拦截此次事件并且在ViewGroup 1 的onTouchEvent返回true消费这次事件。
    红色的箭头代表ACTION_DOWN 事件的流向
    蓝色的箭头代表ACTION_MOVE 和 ACTION_UP 事件的流向

    一下子画了好多图,还有好几种情况就不再画了,相信你也看出规律了,对于在onTouchEvent消费事件的情况:在哪个View的onTouchEvent 返回true,那么ACTION_MOVE和ACTION_UP的事件从上往下传到这个View后就不再往下传递了,而直接传给自己的onTouchEvent 并结束本次事件传递过程。

    对于ACTION_MOVE、ACTION_UP总结:ACTION_DOWN事件在哪个控件消费了(return true), 那么ACTION_MOVE和ACTION_UP就会从上往下(通过dispatchTouchEvent)做事件分发往下传,就只会传到这个控件,不会继续往下传,如果ACTION_DOWN事件是在dispatchTouchEvent消费,那么事件到此为止停止传递,如果ACTION_DOWN事件是在onTouchEvent消费的,那么会把ACTION_MOVE或ACTION_UP事件传给该控件的onTouchEvent处理并结束传递。

    ** tips : 最近刚做了一个自定义控件,里面做了不少事件分发的处理和交互,个人是觉得可以当做本篇文章的一个实践,大家可以看下源码事件分发相关的的部分代码,可能更有体会,链接地址:CalendarListView**

    注:【转载请注明,问题可提问,喜欢可打赏,博客持续更新,欢迎关注】

    相关文章

      网友评论

      • 5298d6ac810a:你好,我觉得你可能就第一个图改了,后面的图跟第一个图说明的东西有很大不同,就Activity向下传递的方法来说就已经开始不一样了
      • KT_11:以前看过,似懂非懂,现在想要彻底攻克弄懂分发机制,参考了大量博文,总的来说楼主流程图还是比较清晰的,其实第一张图就是本文的重点,建议跟着图多走几遍流程,就会对分发流程有一个完整的了解,至于涉及源码的细节可以说是百度一大把了,光看源码更加记不住,感兴趣的自行查找吧。
      • wudouxingjun:看了很多博客,这篇无疑使总结的最好的,如果在加上onToucheListener和onClick以及onLongClick的分析,事件cancel这块目前不是很明白
      • 宋院林:如果能结合实际的例子来讲解,效果可能会更好
      • 鬼谷子先生:问题请教:还是事件分发的时候,如果在事件流的底部也就是View.onTouchEvent()函数中写switch语句事件流中MoniterEvent.DOEN直接return true消耗掉事件流,其他情况return super.onTouchEvent()。在ViewGroup.onTouchEvent()函数中写switch语句当MoniterEvent.move的时候return true。最后的现象为什么是MoniterEvent.move沿着U型事件流一直到VIew.onTouchEvent()函数,跳过View.onTouchEvent()函数直接到Activity.onTouchEvent()函数呢。
      • 刘小厨:有个问题请教一下:当我使用RecyclerView+ViewPager+RecyclerView展示的时候(京东->我的界面->下拉),当我手指持续向上滑动,怎么在滑动过程中(手指不抬起),将时间交给内层的RecyclerView处理呢??我在应该应该将事件交给内层RecyclerView处理的时候调用了外层RecyclerView的requestDisallowInterceptTouchEvent(true).但是没有效果
      • lynch0571:已收录《千赞专题》,欢迎关注:http://www.jianshu.com/collection/032a478c3dbf
      • c82bf09cf357:我不知道那些看懂了的人怎么想的,是背过了?一是不全,二是没有深究,三是有漏洞,四是没有实用性等等,无意冒犯,我也有收益的,嗯。
        这一篇顶多算记住,更像是背过,理解并且运用分发机制,我们还得去别的地方,博采众长。
        kangaroo9997:我觉得博主讲的很好了,先是把大概的流程讲清楚了。
      • 侦探小柯南:先理解事件调用的顺序,再去理解代码中是如何实现的,good,谢谢博主的一篇美文
      • 木溪bo:每看一遍都感觉不一样,收获不少
      • 吴小博Toby:写的很好
      • 笑羋:很容易说清楚的问题,尽量不要用这么长的篇幅去描述,我一定相信你是吃透了,但是看着很费劲。
      • 3a71abf36d55:其实很多码农讲解的都不好,因为他们是码农,没有作者的修养,什么排比,比喻,夸张等修辞手法都没有,这样的文章,你叫人怎么记得住,所以看不懂的其实很正常,看懂的才说明这人脑子有洞
        kangaroo9997:赞同你的观点,其他的真是讲的一坨屎。胡乱贴源码,还讲不清楚。
      • 不用98K:这个图是真的强啊!
      • 青林_64bf:去看过源代码的博客,看的似懂非懂,关键主要的流程没总结起来,看了你这边的图解,结合以前的源代码知识点,感觉思路一下子就清晰了,感谢博主为我理清思路...
      • Todo2:写的非常不错,赞
        组件化和插件化的开发里程总结
        https://www.jianshu.com/p/df2a6717009d
      • Todo2:组件化和插件化的开发里程总结
        https://www.jianshu.com/p/df2a6717009d
      • 杨杰C:我写了一篇大家提提建议https://www.jianshu.com/p/709c8a2fbed8
      • 千万bt:博主你好,有个问题想不太明白,假如在viewgroup里面的dispatchTouchEvent方法,方法内部单独调用super.dispatchTouchEvent(event),然后自己设置返回值return false,事件会继续传递吗。然后super.dispatchTouchEvent(event)这个方法具体是做了什么,是否必须调用,麻烦解答下哈,小白不是很懂源码
        有兴不虚昧:调用super就是调用系统默认的事件分发方法,这时候down事件是无论如何都会分发到最顶层的子View的,但是后面false了,就意味着你的自定义ViewGroup的在分发down事件的时候没有找到要处理的子View,就调用了自己的onTouchEvent(),然后这个onTouchEvent()的返回值又决定了自定义ViewGroup的父View的down事件返回值,父View的dispathcEvent()又决定了爷View的dispatchEvent()的返回值,这样不断的回朔到最顶部的Activity的dispatchTouchEvent()返回了false,Activity就调用了onTouchEvent(),既然down事件都没有子View要处理,move和up事件,从Activity传递到最顶部的ViewGroup:DecoverView的时候,decoverView的onTouchEvent()会被调用,只是这里的Log没有打印出来而已。ViewGroup默认是不处理事件的,所以DecorView的dispatchEvent()的在move事件的时候返回false,又决定Activity的dispatchEvent()的move事件返回false,Activity就调用了自己的onTouchEvent()处理move事件,up事件同理!所以down事件很重要,决定了move和up事件是否往下传递。还是要看源码的,可以看看我写的:https://www.jianshu.com/p/51239c2bc43d
        Max____:第一种应该和直接返回super是一眼的,因为返回super其实就是执行一遍super里面的逻辑,然后返回false。
        第二种就有点复杂了...(我会说我也没搞懂?)
        Max____:在ViewGroup的dispatchTouchEvent中先执行super,再返回false,打印日志如下
        ↓ Activity dispatchTouchEvent down
        ↓ ViewGroup dispatchTouchEvent: down
        ↓ ViewGroup onInterceptTouchEvent: down
        ↓ TextView dispatchTouchEvent down
        ↓ TextView onTouchEvent down
        ↓ ViewGroup onTouchEvent: down
        ↓ Activity onTouchEvent down
        ↓ Activity dispatchTouchEvent move
        ↓ Activity onTouchEvent move
        ↓ Activity dispatchTouchEvent up
        ↓ Activity onTouchEvent up
        在ViewGroup的dispatchTouchEvent中先执行super,再返回true,打印日志如下
        ↓ Activity dispatchTouchEvent down
        ↓ ViewGroup dispatchTouchEvent: down
        ↓ ViewGroup onInterceptTouchEvent: down
        ↓ TextView dispatchTouchEvent down
        ↓ TextView onTouchEvent down
        ↓ ViewGroup onTouchEvent: down
        ↓ Activity dispatchTouchEvent move
        ↓ ViewGroup dispatchTouchEvent: move
        ↓ ViewGroup onTouchEvent: move
        ↓ Activity dispatchTouchEvent move
        ↓ ViewGroup dispatchTouchEvent: move
        ↓ ViewGroup onTouchEvent: move
        ↓ Activity dispatchTouchEvent up
        ↓ ViewGroup dispatchTouchEvent: up
        ↓ ViewGroup onTouchEvent: up
      • Michael_WJ:U型图貌似有点问题,view执行return super.onTouchEvent(event)和true默认应该都是消费事件,而return false才是回溯到父view
        谷哥得小弟:return super.onTouchEvent(event)默认就是false。。。。才写代码测试过
        LittleBug:view 和viewgroup的 都是如你所说
        已经demo验证过了
      • 飞信FeiXin:看的我头晕晕的,心好累呀:pensive:
        飞信FeiXin:有道理
      • 84637d1af8a2:总体说来没问题,但是有些说的太绝对了,

        如果 ViewGroup 的方法
        @Override
        public boolean dispatchTouchEvent(MotionEvent ev) {

        super.dispatchTouchEvent(ev);
        return true;
        }
        这时,事件还是会向下传递的,所有说,应该考虑是否调用过父类的方法
        风_b6c8:说的没毛病。应该说成不调用父类的dispatchTouchEvent情况下直接返回true,表示消费,终止传递事件。
        千万bt:是的,有些比较特殊的情况博主貌似没有考虑到
      • 372b00163b4e:总结的很好,之前看了源码结合这篇图解加深了认识~😬
      • Dhaojie6688:我看了半天不知道什么情况下或者怎么手动更改dispatchTouchEvent和onInterceptTouchEvent的返回值?
      • AWeiLoveAndroid:膜拜大佬:+1:
      • yaku2017:建议作者把getParent().requestDisallowInterceptTouchEvent(true)的影响也补充到文章中,这样会更清晰一些。
      • d8184ca3c970:写的真牛 b,特别是图,一目了然,大神不当老师真是祖国花朵之不幸,我央央大国之不幸!!!
      • valleyPioneer:真的实用
      • 604a54fbdc76:最后的12张图确实很强大
      • 码农一颗颗:我觉得这个super调用父类,改成默认实现比较好吧
      • Rex_叶然:虽然很多人点赞,我觉得是又臭又长,你花大篇幅整理了怎么分发 却没有点明为什么会这样。很多新人估计看一遍觉得不错,实战或者去面试,依然啥都记不住不知道,也不知道为什么。说到底还是个返回值传递的问题。相互是个什么干扰,你列举这么多情况反而是一种记忆负担。

        我举例ViewGroup的dispatchTouchEvent你如果点明 内部是

        ViewGroup
        View[] views=getChildView();
        for(int i=0;i<views.length;i++){
        //判断下Touch到屏幕上的点在该子View上面
        if(...){
        if(views[i].dispatchTouchEvent(ev))
        return true;
        }
        }

        view的内部dispatchTouchEvent其实就是自己的onTouchEvent
        View
        public boolean dispatchTouchEvent(MotionEvent ev){
        ....//其他处理,在此不管 return onTouchEvent(event);
        }
        这就不容易解释为什么不拦截的情况下,就是子不要父才能要 只要一个子要,就不继续往下执行,父就不能要的吗 因为直接return了 循环终止了。
        鬼谷子先生:问题请教:还是事件分发的时候,如果在事件流的底部也就是View.onTouchEvent()函数中写switch语句事件流中MoniterEvent.DOEN直接return true消耗掉事件流,其他情况return super.onTouchEvent()。在ViewGroup.onTouchEvent()函数中写switch语句当MoniterEvent.move的时候return true。最后的现象为什么是MoniterEvent.move沿着U型事件流一直到VIew.onTouchEvent()函数,跳过View.onTouchEvent()函数直接到Activity.onTouchEvent()函数呢。
        kangaroo9997:我觉得博主讲的很好了,先是把大概的流程讲清楚了。然后具体的也可以深究源码实现
        难道贴一堆源码,然后讲的一头雾水,没讲清楚好吧。 要是真的研究源码。我还不如自己去看。不用别人贴
        ccfae98fbabb:@这世界动听的旋律 其实博主说的挺好,只是理论意义更大,虽然后半部分写的有点多,但是让人不看代码也能懂。您的想法是直接看了代码的实现,让人们了解了整个具体过程,你们两个的论述方法一结合让我这个小白恍然大悟
      • Allen_tong:View里面的onTouchEvent()返回super事件不会再往上传递了,就此结束了,改一下吧。确定是写了代码测试吗?
      • 1006b265d05c:请问下,你的View的onTouchEvent里return super.onTouchEvent(event),还会把事件给父控件的onTouchEvent吗?我测试了好像不行。
        风_b6c8:会的,我刚测试过。
      • 7c043ac85ac4:明儿面试,今天看看,不错,受教了
      • Davisxy:楼主你好,有个问题想请教一下,就是在ViewGroup的onTouchEvent中的ACTION_MOVE和ACTION_UP必须是一块分发下来的吗?就是只要发生ACTION_MOVE就一定会发生ACTION_UP事件吗?我的问题来源于一次测试,我在ViewGroup中的interceptTouchEvent的ACTION_DOWN中拦截了事件,这时就到了ViewGroup的onTouchEvent中的ACTION_DOWN中,然后我在该ACTION_DOWN中返回了true,也就是消费了这个事件的ACTION_DOWN事件,然后在下一波ACTION_MOVE到来时,一样拦截到了ViewGroup的onTouchEvent的ACTION_MOVE中,这时我返回了false,按照我理解的,不应该是事件终止了吗,但是我测试的结果是还有一波ACTION_MOVE和ACTION_UP事件到来了,实在不懂这块,请教一下,谢谢!!!
        Davisxy:测试的结果是还有一波ACTION_MOVE和ACTION_UP事件到来了,不但到来了,还消费了,所以说是不是一旦他消费了down事件,返回了true,后续的事件就打死在这了,move和up中返回什么都无所谓了,肯定消费???(子View改变标志位那个暂时不管)
      • 会思考的鸭子:看了两三遍,终于算是明白了
      • 望北8261:一般都不会重写 dispatchTouchEvent 方法
      • Alan_Mo:写的很好,学习了。
      • itkluo88:看得最明明白白的一次
      • zekers:讲的太好了,以前模糊的概念一下子就明白了,谢谢楼主分享
      • 26dcfb9e064c:又看了一遍,整体都很👍
      • 26dcfb9e064c:后半部分分析的很赞!
      • 9d00493d0c2b:谁能解答为什么Activity的OndispatchTouchEvent返回true或者false都不会调用onTouchEvent方法,只有super.dispatchTouchEvent(ev)才会继续往下走??:no_mouth:
      • b984c31ca8ea:尼玛 图里面有个直接线段是歪的 我以为我眼睛出问题了
      • 池塘细雨:大家讲了事件流程,实际上最重要的为什么这么这样的设计,这样设计的好处,设计的要求却没人说
      • 230d2ac1f30e:请问一下,您用的什么作图工具:smile:
      • df75d933e7b9:作者,你的分析是针对Android5.0之前的源码写的吗?
        为什么我在看5.0的源码,ViewGroup中onInterceptTouchEvent方法返回true时,在ViewGroup中没发现有调用自身的onTouchEvent()啊。。
      • doc__wei:OnDispatchTouchEvent 直接返回true或者false???,,看到这个方法返回true或者false,我果断就不看了,,,,你真行。呵呵
      • 永正:请问一下画图是用什么软件的?
      • 程序员小跃:看到几个评论说作者还需要再仔细说点,或者对照源码说说。其实作者能分享就很不错了,我只是拿来看看,学习学习,强化的地方还是需要手动实践,感谢楼主
      • 健胃消食片165:如果一个点击事件发在UI View 上,View 的onTouchEvent中如果返回super,调用流程和true是一样的,只过不该view 的OnClickListener 却没有被触发。
      • 有兴不虚昧:move和up事件的流向有问题
      • eca9dbebdd0b:没源码。。。先去看个源码再来看
      • 5b9f171305a8:我有一个疑问 “关于ACTION_MOVE 和 ACTION_UP” 这个这个章节的前两个例子中,你在dispatchTouchEvent方法返回true消费事件,导致后面的MOVE和UP事件也只传递到该控件的dispatchTouchEvent而没有传递到onTouchEvent。是因为你的dispatchTouchEvent方法一律只返回true的吗?如果dispatchTouchEvent只对DOWN事件返回true,那么后面的MOVE和UP事件是能传递到onTouchEvent的吧。
        5b9f171305a8:@Kelin 那这一点上胡凯的这篇http://hukai.me/android-deeper-touch-event-dispatch-process/ 跟你的结论不一样了,你直接看到文章底部Case2这一节,他说当ViewGroup的dispatchTouchEvent只对Down返回true,其他返回super,那么MOVE和UP是能传到onTouchEvent的。可能我理解的不对,还请指教。
        Kelin:后面的MOVE和UP事件是不能传递到onTouchEvent的,你可以看看后面关于ACTION_MOVE 的图解:smile:
      • 有兴不虚昧:说实话,如果只看这个还是不行,还会有很多细节搞不清楚,建议看了的同学再去看看这篇http://blog.csdn.net/yanbober/article/details/45887547/.dispatchTouchEvent()有直接就返回true或false的,也有先调用父类的,再返回true或false的。但这篇文章里面说的返回true或false没说明白。dispatchTouchEvent()调用父类的其实就是走onTouch-->onTouchEvent(),dispahchTouchEvent()的返回值不管是view还是viewgroup都是一样的决定了move,up事件的接收
      • 吃透Java:看了博主的文章,差点源码解析,自己写了一篇附带源码解析:http://blog.csdn.net/u013277209/article/details/71600419
        Rex_叶然:@Jafir 你说的对。博主核心都没展现出来,列举这么多情况反而是记忆负担
        Jafir:看了一下,前面写的还可以,后面有些仓促。
        一些细节也不太清楚,别人问了问题你可以去试着解答一下。
        比如:
        1、对于如果onIntercept返回true拦截了,交给onTouchEvent去处理,具体体现在何处
        2、判断子View是否能够接收事件从哪里体现,可以详细一点
        3、另外一个比较重要的方法dispatchTransformedTouchEvent也可以分析一下
        4、viewGroup和view的dispatch返回false,会直接回溯到parent的onTouchEvent,这个又在哪里体现,也可以写一下
        我认为再把这几点搞清楚就更完美了
        Jafir:正想跟博主说 要是有附加一点源码就更好了,这里就有了,我看看
      • 吃透Java:参考了你的文章,我又添加了源码分析http://blog.csdn.net/u013277209/article/details/71600419
      • 性冷淡_:所以在看图之前,需要先阅读下源码。然后自己再结合图再总结一次
      • youyuge:黑马程序员2017.1出版的android面试宝典P98,图和楼主第一张图几乎一样只是方向变了,下面的说明也很相似。。。不过应该算不了侵权lz吧
      • 6fd1d9051260:作者你好,我写的时候如果view的onTouchEvent返回值为super.onTouchEvent(event)时,事件是被消费了的。和return true结果是一样的。是我写错了吗?
      • 性冷淡_:Mark一波~
      • 891da7536fbb:onTouchEvent()返回false,是不是会导致dispatchTouchEvent()返回false,这样才会执行父类的onTouchEvent()。
        891da7536fbb:@Kelin 我表述错了,是子视图的onTouchEvent()返回false,会导致子视图的dispatchTouchEvent()返回false,所以才会执行到父视图的onTouchEvent()。并且子视图的dispatchTouchEvent()和父视图的onTouchEvent()都应该是在父视图的dispatchTouchEvent()里面得到调用的。
        Kelin:@Chan_Jpeng 不会的,这个没关系的 不会再去执行dispatchTouchEvent的 不然就执行两次了
      • 非墨Zero:请问图用什么画的?
        Kelin:@非墨Zero Xmind
      • 心狼:听不错的 看了之后有种茅塞顿开的感觉 对事件分发机制有了解了不少
      • ab64fd4eaee3:如何转载,大神写的太好了
      • Venbro:有些错误没改过来,activity的dispatchTouchEvent返回true/false事件已经被消费了不再往下传递
      • 和风拂夜:谢谢,看完文章,写个demo,打印日志,豁然开朗。
      • 7b5f1ae24ff9:纯记忆应用,而不知道细节。。源码的话比较理解,但就是源码的话需要自己去刻画那些逻辑,自己去联想。。而且需要自己总结一下在应用中的结论。会比较麻烦。。
      • 梦想编织者灬小楠:看了几遍了,每一次都有不同感受...
      • 5b10c93bff6e:写的是挺详细的,不过如果能够配上源码的分析过程以及例子的结合讲解应该就是完美了。
      • 653d1ebd6d6d:首先看下ViewGroup 的dispatchTouchEvent,之前说的return true是终结传递。return false 是回溯到父View的onTouchEvent,然后ViewGroup怎样通过dispatchTouchEvent方法能把事件分发到自己的onTouchEvent处理呢,return true和false 都不行

        ps:ViewGroup 的dispatchTouchEvent,return true代表处理事件,难道不调用onTouchEvent方法处理事件吗?
      • 酒足饭饱睡觉觉:敢问大神的流程图用什么软件制作的,谢谢
      • flyrun1:文章里不包含 在传递 MOVE ,UP 事件时,不同的方法 返回 false true 和super时 不同处理情况 ,想想好像挺复杂。@Kelin
      • 老匡话Android:第三第四张图还是没改过来
      • WilsonMing:想问为什么Fragment没有dispatchTouchEvent(), onInterceptTouchEvent(),onTouchEvent()事件回调接口?如何理解这个设计?
      • smashing_chen:之前对这块总是懵懵懂懂的,看了这篇文章感觉清晰了很多~感谢作者啊,用图片表达的方式真的很不错!赞!!!
      • 7ce0496aa138:楼主你好,我想问下我按你说的 都是默认实现的 流程是走玩 了,只是不明白为什么最后又走一次activity的dispatchTouchEvent 和onTouchEvent。
        log如下:
        MainActivity: MainActivity dispatchTouchEvent
        MyReativeLayout: viewGroup dispatchTouchEvent
        MyReativeLayout: viewGroup onInterceptTouchEvent
        myView: dispatchTouchEvent
        myView: onTouchEvent
        MyReativeLayout: viewGroup onTouchEvent
        MainActivity: MainActivity onTouchEvent
        MainActivity: MainActivity dispatchTouchEvent
        MainActivity: MainActivity onTouchEvent
        7ce0496aa138:明白了 ,原来前7个是down事件没有被消费,所以最后两个是自己消费的自己的move和up事件:smile:
      • amazingokc:我实验了下,在自定义viewpage的onInterceptTouchEvent()返回false,是回走到自己的onTouchEvent(),跟作者这里说的一点冲突了。求解
        amazingokc:自己仔细研究了下,发现,如果viewpage如果没有子view的情况下,onInterceptTouchEvent()返回false的时候会调用自己的onTouchEvent(),如果有子view的情况下就不会调用。
      • Terry:虽然写的很不错,但是如果能有个目录,有个着重点,分类来讲会更好些
      • Xuhb:最后没有总结,把问题简化一下
      • Xuhb:activity的dispatchTouchEvent事件写得有问题,让我很困惑。你说返回true或false都是消费,调用super是分发到viewGroup的dispatchTouchEvent,那肯定是有问题的,因为super其实最终还是会返回true或false,只是里面有一些逻辑,有些是返回true,有些返回false。按照你这个图,永远不可能分发下去了。希望博主能改一下
      • 8543785d078d:楼主你好,我在测试中发现,全部走super方法,DOWN事件在传递到View的onTouchEvent方法后就终止传递了,在翻看源码后发现,在View的onTouchEvent方法中如果(viewFlags & CLICKABLE) == CLICKABLE ||(viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) ||(viewFlags & CONTEXT_CLICKABLE) == CONTEXT_CLICKABLE)如果该控件是可点击的,这个条件成立,就会返回true,事件不再传递,因此如果在View的onTouchEvent返回super且该view可点击,则DOWN事件就停止了向上传递
        Kelin:@再见孙悟饭 恩恩 如果设置可点击的,事件就会被消费了,不会继续向上传递了,click事件的处理也是在View的onTouchEvent做的:smile:
      • 有兴不虚昧:第一次在简书赞赏,期望你分享的好东西
      • bdf080fa6676:你好,我试了下,View.onTouchEvent()返回 super 时,父容器中的onTouchEvent()的输出语句并没有打印是什么情况?
      • 37a317bc9f3e:看了那么多事件分发的文章,就这篇看懂了!!!
      • 806ed274a668:事件分发看了好多,大神这篇最清晰易懂
      • coder_yu:流程确实很清楚了,要是再配合一些标志位和clickListener、touchListener之类的影响更好啦~
      • C_Sev:奥
      • kajibu:功德无量 太感谢了:+1:
      • dc31f04762e9:很好,比网上其他人的清楚
      • 0bc95b58ed61:博主真的好赞,写得很详细,非常受用。提一个问题,如果我在ViewGroup 调super.dispatchTouchEvent 但是我返回true, 会继续分发,子view 没有消耗事件,最终会在ViewGroup onTouchEvent中消费。 你的图中true直接消费估计是没有考虑到这种例子,还是消费也包括了这种情况;子view 如果消耗了, 分发就停在了子view消费的地方,所以dispatchTouchEvent return true 不一定就能消费事件。
        Kelin:有点不太明白你说的这句话”如果我在ViewGroup 调super.dispatchTouchEvent 但是我返回true“,是指什么? 如果在ViewGroup 的dispatchTouchEvent return true时间就会被消费,如果调super.dispatchTouchEvent 会走到onInterceptTouchEvent,具体你可以再描述详细些,我再看看呢:smile:
      • 清心极乐:Kelin你好:本来想跟你私聊的,不知道怎么私聊SO只在这留言了,分发机制的文章真的受益良多,但是在写Demo的时候发现两个地方与你写的有点不同。1、在View的onTouchEvent里只有return false将事件传到父控件的onTouchEvent而return super.xxx()和return true;是将事件消费掉。2、在ViewGroup的onTouchEvent里return true 此控件以下的View的Down事件并没有触发,看了你的截图View以下的Down事件也触发了,这和我测试的不一致。希望得到你的回复,谢谢。
        Kelin:@清心极乐 你看一下最上面的图或者第三张图,ViewGroup的onTouchEvent方法被调用的三个来源,不一定子View的onTouchEvent一定会被调用,当然其中一个来源是此控件以下的View的onTouchEvent方法,onTouchEvent 的事件流是从下往上的,你再仔细看下图哈
        清心极乐:@Kelin 你好,先说第一个问题吧,你的意思是不同控件是否传递到父控件是不一样的,我的Demo用的是TextView,return super.xxx()就直接消费掉了,第二个问题是如果父控件的onTouchEvent方法里return true那么down事件不会再传递给子控件。
        Kelin:@清心极乐 你好,第一个问题 return super.xxx() 是消费掉还是传回父控件,这个取决你的View的上层实现,如果你的View是button那么默认是return true的,当然你的ViewGroup收不到了,第二个问题不太懂你想表达的意思,你可以再说详细一些吗? :smile:
      • MC_Honva:对事件分发机制更加了解了。大谢,手下收地的膝盖和进贡 :stuck_out_tongue_closed_eyes:
      • xm_585:可以,很清晰,学习了!
      • 小猪在哪:非常清晰
      • 22ee03b8dc8f:难虽然不难

        但是楼主你用如此简单的话就描述出来了

        简直叼叼叼
      • 746c51bb9a52:您好 http://blog.csdn.net/lfdfhl/article/details/51603088
        这篇文章说: 如果在View的onTouchEvent方法中返回super.onTouchEvent的话 那么该View的父容器就不会接收到这个事件了
        但是您这个文章中说onTouchEvent如果返回super.onTouchEvent,那么这个事件还会传到它的父容器的onTouchEvent中 您可以大概看看这篇文章 不知道那个对的?还是我理解错了
        Kelin:@13699149987 这个简单,你写个Demo验一下就最清楚了也最信得过,几分钟的事 :smile: 我只能说 我的肯定没问题,因为我写Demo看输出的log了
      • 746c51bb9a52:拜读了您的文章 受益匪浅 请问能否再讲讲onTouch onClick onTouchEvent 这几个方法的区别
        993ba7e014d8:@Kelin 楼主我想问一下就是其他的不变,但是我的onTouchEvent中的三个事件,一个true,另外一个false,一个true,应该会怎么样子啊,还有就是requestDisallowInterceptTouchEvent如果是在前面的话也就是说这个方法的执行顺序是在interceptTouchEvent上面的吗
        Kelin:@13699149987 我看了源码,如果requestDisallowInterceptTouchEvent true) interceptTouchEvent就不会调了,ACTION_MOVE和ACTION_DOWN 应该不会有变化
        746c51bb9a52:@13699149987 补充请教下 requestDisallowInterceptTouchEvent(true)这个方法 如果我在子View中请求父View不拦截某个事件 那是不是父View的interceptTouchEvent()即使返回true 这个父View也不会拦截这个事件了?另外这个方法对ACTION_MOVE和ACTION_DOWN事件的传递会不会起到变化作用?

      本文标题:图解 Android 事件分发机制

      本文链接:https://www.haomeiwen.com/subject/jjlvdttx.html