Android事件机制(二)

关于Android事件机制,很多大神都做了分析,我也是通过他们的帖子进行学习,在此特向他们表达谢意。
Android事件机制(一)从代码演示的角度整理了一下我对Android事件传递的理解,下面就通过源码浅显的分析一下个人理解(想想源码,就觉任重而道远,笑cry)。

那么来看一下上篇中提到的,“有触摸,就有dispatchTouchEvent方法的调用”,这个dispatchTouchEvent方法。
虽然dispatchTouchEvent方法是事件分发的第一步,但是一般情况下不太会去改写这个方法,只是onInterceptTouchEvent、onTouchEvent、OnTouchListener.onTouch这几个方法都会被其以不同的条件调用,这也就决定了事件如何传递。

点击MyView,首先会调用到MyView最高父辈的dispatchTouchEvent方法,这里从MyViewGroupA.dispatchTouchEvent开始,而MyViewGroupA继承自RelativeLayout,因此我们进入到了ViewGroup.dispatchTouchEvent

当事件到来的时候,ViewGroup.dispatchTouchEvent会先判断是否要拦截事件:

ViewGroup.dispatchTouchEvent

从2094行可以看出,事件机制从MotionEvent.ACTION_DOWN开始,此时会重置所有状态。
从2014行开始,对是否拦截的boolean变量进行判断赋值。

如果不拦截:

ViewGroup.dispatchTouchEvent

从2164-2221行就会对子View进行遍历,找到处于点击范围的合适的子View,找到之后,通过2197行的dispatchTransformedTouchEvent方法来调用child的dispatchTouchEvent方法,并获取它的返回值,根据返回的值来判断子View是否成功消耗了事件,如果返回的是true代表成功消费,那么就会对mFirstTouchTarget进行赋值,从而不会进入2238行的条件判断,不会调用super的dispatchTouchEvent方法,事件也就停止了传递:

ViewGroup.dispatchTouchEvent

如果child没有消耗事件,即child的dispatchTouchEvent方法返回了false,或者没有发现合适的子View,即child == null,那么就不会给mFirstTouchTarget进行赋值,即mFirstTouchTarget == null,通过dispatchTransformedTouchEvent方法就会调用到super的dispatchTouchEvent方法。

如果进行了拦截,那么直接通过2238行进而执行2240行,调用super.dispatchTouchEvent方法,自己处理事件。

dispatchTransformedTouchEvent的部分源码如下:

dispatchTransformedTouchEvent

经过上述分析,可以知道:
如果不设置拦截,MyViewGroupA的直属child是MyViewGroupB,因此MyViewGroupA就会调用到MyViewGroupB的dispatchTouchEvent方法,而MyViewGroupB的直属child是MyView,所以就会调用到View的dispatchTouchEvent方法。
假设MyViewGroupA或者MyViewGroupB设置了事件拦截,那么就会调用ViewGroup的super.dispatchTouchEvent,而ViewGroup继承自View,所以最终还是调用了View的dispatchTouchEvent方法。

那么接下来就看一下View.dispatchTouchEvent方法:

View.dispatchTouchEvent

可见,上面说的onTouchEvent、OnTouchListener.onTouch会被dispatchTouchEvent调用,不是瞎掰的。
通过黄框圈中的代码可以很明显的看出View.dispatchTouchEvent的返回值受onTouchEvent、OnTouchListener.onTouch影响,并且OnTouchListener.onTouch优先于onTouchEvent

源码就先看到这里,来捋一下上篇中Log_1所示的流程执行:

Log_1

点击MyView,触发ACTION_DOWN
(1) 首先调用MyViewGroupA.dispatchTouchEvent,在MyViewGroupA.dispatchTouchEvent中调用MyViewGroupA.onInterceptTouchEvent,返回false,不拦截事件;
(2) 找到MyViewGroupA的子View,即MyViewGroupB,调用MyViewGroupB.dispatchTouchEventMyViewGroupB.dispatchTouchEvent在执行中调用MyViewGroupB.onInterceptTouchEvent,返回false,不拦截事件;
(3) 找到MyViewGroupB的子View,即MyView,调用MyView.dispatchTouchEvent,由于没有设置OnTouchListener,因此在MyView.dispatchTouchEvent中就会调用MyView.onTouchEvent,而MyView.onTouchEvent这个方法返回false,因此就使MyView.dispatchTouchEvent返回false,也就是说,MyView没有消费这个事件;
(4) 接着,MyViewGroupB.dispatchTouchEvent中接收到MyView返回的结果,发现MyView没有消费事件,mFirstTouchTarget就不会被赋值,即mFirstTouchTarget == null,那么根据ViewGroup.dispatchTouchEvent源码的第2238行,就会去调用super.dispatchTouchEvent,进而调用到MyViewGroupB.onTouchEvent这个方法,而这个方法返回false,因此就使MyViewGroupB.dispatchTouchEvent返回false,也就是说,MyViewGroupB也没有消费这个事件;
(5) 最后,MyViewGroupA.dispatchTouchEvent中接收到MyViewGroupB返回的结果,发现MyViewGroupB没有消费事件,mFirstTouchTarget就不会被赋值,即mFirstTouchTarget == null,那么根据ViewGroup.dispatchTouchEvent源码的第2238行,就会去调用super.dispatchTouchEvent,进而调用到MyViewGroupA.onTouchEvent这个方法,自己处理和消费事件。
(注:这个过程画图应该会更清晰,这里我是按照自己的语言分析顺序写的,文末的参考链接中有图,而且很清晰)

在事件序列中,总是以ACTION_DOWN开始,如果不是ACTION_DOWN,说明事件序列已经开始传递了。如果对ACTION_DOWN不消费,那么就可以理解为,我不要这个事件,也就没有了接下来ACTION_MOVE、ACTION_UP的传递和处理。

这一篇就先分析到这里,太长了看着会厌烦,下一篇分析一下onTouchEvent返回true的情况。

参考:
Android事件分发机制完全解析,带你从源码的角度彻底理解(上)
Android事件分发机制完全解析,带你从源码的角度彻底理解(下)
Android事件分发机制详解
Android群英传-事件拦截机制分析

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容