事件分发勘误:当前View的TouchTarget是什么

前言

这里先给你们道个歉! 上一篇文章为啥还在聊:事件分发?还不是由于不会!,其实有一个严重的错误。不知道给小伙伴们带来了多少困扰。那就是我在上一篇文章中说当前View的TouchTarget记录的是消费当前事件的View。其实这是不对的面试

因此今天这篇文章首先是纠错,其次是完全捋一下TouchTarget到底指的是什么,以及事件分发的全流程。post

正文

上文我们从源码看到,若是当前View的mFirstTouchTarget不为null,那么当前的TouchEvent事件,就会直接分发到mFirstTouchTarget中记录的View上(也就是调用这个View的dispathTouchEvent())。学习

那么假设mFirstTouchTarget为消费这个事件的View,那么事件就会从根View直接分发到消费事件的View,也就是说事件直接跳过全部中间的层级的View(“没有中间商赚差价”)。不过我猜有经验的小伙伴都会知道,这个错的,不会越过中间的View。spa

没错,事实上这个假设的确是错的!3d

Log打脸

代码中分别是ViewGroupA中嵌套ViewGroupB,ViewGroupB嵌套了ViewC。全部的分发方法都直接返回super的实现,只有ViewC的onTouchEvent()方法返回true。此时咱们点击滑动ViewC...code

ViewGroupA -> dispatchTouchEvent -> MotionEvent.ACTION_DOWNcdn

ViewGroupA -> onInterceptTouchEvent -> MotionEvent.ACTION_DOWNblog

ViewGroupB -> dispatchTouchEvent ->MotionEvent.ACTION_DOWN事件

ViewGroupB -> onInterceptTouchEvent -> MotionEvent.ACTION_DOWN开发

ViewC -> dispatchTouchEvent -> MotionEvent.ACTION_DOWN

ViewC -> onTouchEvent -> MotionEvent.ACTION_DOWN

ViewGroupA -> dispatchTouchEvent -> MotionEvent.ACTION_MOVE

ViewGroupA -> onInterceptTouchEvent -> MotionEvent.ACTION_MOVE

ViewGroupB -> dispatchTouchEvent ->MotionEvent.ACTION_MOVE

ViewGroupB -> onInterceptTouchEvent -> MotionEvent.ACTION_MOVE

ViewC -> dispatchTouchEvent -> MotionEvent.ACTION_MOVE

ViewC -> onTouchEvent -> MotionEvent.ACTION_MOVE

ViewGroupA -> dispatchTouchEvent -> MotionEvent.ACTION_UP

ViewGroupA -> onInterceptTouchEvent -> MotionEvent.ACTION_UP

ViewGroupB -> dispatchTouchEvent ->MotionEvent.ACTION_UP

ViewGroupB -> onInterceptTouchEvent -> MotionEvent.ACTION_UP

ViewC -> dispatchTouchEvent -> MotionEvent.ACTION_UP

ViewC -> onTouchEvent -> MotionEvent.ACTION_UP

咱们会发现,Touch事件是按照View层级的顺序一级一级的去传递。所以咱们能够得出一个结论:

当前View上的mFirstTouchTarget颇有可能记录的是它子View中可能消费事件的那个View。

Debug求证

接下来,让咱们断点到ViewGroupA上的dispathTouchEvent()一看究竟:

的确如此,当前View的mFirstTouchTarget就是记录它子View中那个命中消费事件的View。

所以基于这个结果,事件分发的流程就真正串起来了!

上篇文章咱们知道事件分发的逻辑一切都在根View的dispatchTouchEvent()中完成。此时只把场景限制到ViewGroupA->ViewGroupB->ViewC中,咱们来看一下分发是如何完成的。

梳理

事件DOWN来到ViewGroupA的dispatchTouchEvent(),此方法中因为本身onInterceptTouchEvent()返回false,所以将经过事件的x、y来交给符合此范围的子View继续去分发此事件。可是此时谁都不知道这个事件能被谁消费

找到子View后(也就是ViewGroupB),调用ViewGroupB的dispatchTouchEvent()。对于ViewGroupA来讲,ViewGroupB的dispatchTouchEvent()返回值将决定是否有View消费此事件。

  • 若是返回true,说明事件分发成功,mFirstTouchTarget记录为ViewGroupB,之后的事件所有交给它处理
  • 若是返回false,此事件没有View消费,mFirstTouchTarget为null。调用自身onTouchEvent()尝试本身消费,若是不消费,之后事件将再也不传递过来。(由于对于ViewGroupA的上一层来讲,mFirstTouchTarget为null)

ViewGroupB的dispatchTouchEvent()一样要重走拦截这一套,固然自身一样没有拦截,而后事件一样经过x、y交给本身的子View的dispatchTouchEvent(),也就是ViewC。

ViewGroupB一样也在等ViewCdispatchTouchEvent()返回值来决定本身的mFirstTouchTarget

因为ViewC是一个View,而对于View的dispatchTouchEvent()返回值来讲,它取决于自身的onTouchEvent()。由于咱们的ViewC消费了事件,也就是onTouchEvent()返回了true。所以ViewC的dispatchTouchEvent()也返回true。

因此ViewGroupB的经过ViewC的dispatchTouchEvent()返回true,知道了事件被ViewC消费,那么此时mFirstTouchTarget记录了ViewC,后续的事件ViewGroupB就知道分发给ViewC了。所以此时VIewGroupB的dispatchTouchEvent()返回true。

同理,ViewGroupA也就是知道了,分发到本身的事件之后能够分发给VIewGroupB...

就这样,后续的MVOE,UP事件就能够顺利的一层层往下分发了。

总结

所以,有一些问题咱们就能够经过源码去解释了:

问题1:ViewGroupB的onInterceptTouchEvent()返回true,但onTouchEvent()返回false会发生什么?

当前View若是拦截了事件,那么它的dispatchTouchEvent()返回值,将取决子自身的onTouchEvent()。所以此时返回了false。因此VewGroupB的dispatchTouchEvent()返回false。

因此此时对于ViewGroupA来讲,事件在子View这里是没有分发出去的,因此ViewGroupA尝试消费,若是自身onTouchEvent()返回false,那么后续的事件将不过度发过来了。

问题2:ViewGroupB中的dispatchTouchEvent()不掉任何方法直接返回true,会发生什么?

咱们知道VewGroup以及View中dispatchTouchEvent()的实现才是真正进行事件分发的关键。若是咱们ViewGroupB中的dispatchTouchEvent()直接返回true。对于VewGroupA来讲,后续的事件将直接交给ViewGroupB,那是ViewGroupBdispatchTouchEvent()没有调任何方法,所以对于ViewGroupB来讲它除了会回调dispatchTouchEvent()将不会调用onInterceptTouchEvent()onTouchEvent()

尾声

这俩篇文章因此真正意义上把事件分发的基本流程从根本了梳理了一篇。建议各位小伙伴结合着文章本身也看一篇源码...

一篇下来,关于事件分发的面试题绝对不可能难住你...(哈哈,还差一个CANCEL事件,之后有时间再补上)

OK,小伙伴们,若是以为文章不错...我有个小小的请求,我想吃鸡腿了....

我是一个应届生,最近和朋友们维护了一个公众号,内容是咱们在从应届生过渡到开发这一路所踩过的坑,以及咱们一步步学习的记录,若是感兴趣的朋友能够关注一下,一同加油~

我的公众号:咸鱼正翻身
相关文章
相关标签/搜索