关于Android事件机制,很多大神都做了分析,我也是通过他们的帖子进行学习,在此特向他们表达谢意。
Android事件机制(一)从代码演示的角度整理了一下我对Android事件传递的理解,下面就通过源码浅显的分析一下个人理解(想想源码,就觉任重而道远,笑cry)。
那么来看一下上篇中提到的,“有触摸,就有dispatchTouchEvent
方法的调用”,这个dispatchTouchEvent
方法。
虽然dispatchTouchEvent
方法是事件分发的第一步,但是一般情况下不太会去改写这个方法,只是onInterceptTouchEvent、onTouchEvent、OnTouchListener.onTouch
这几个方法都会被其以不同的条件调用,这也就决定了事件如何传递。
点击MyView,首先会调用到MyView最高父辈的dispatchTouchEvent
方法,这里从MyViewGroupA.dispatchTouchEvent
开始,而MyViewGroupA继承自RelativeLayout,因此我们进入到了ViewGroup.dispatchTouchEvent
。
当事件到来的时候,ViewGroup.dispatchTouchEvent
会先判断是否要拦截事件:
从2094行可以看出,事件机制从MotionEvent.ACTION_DOWN
开始,此时会重置所有状态。
从2014行开始,对是否拦截的boolean变量进行判断赋值。
如果不拦截:
从2164-2221行就会对子View进行遍历,找到处于点击范围的合适的子View,找到之后,通过2197行的dispatchTransformedTouchEvent
方法来调用child的dispatchTouchEvent
方法,并获取它的返回值,根据返回的值来判断子View是否成功消耗了事件,如果返回的是true代表成功消费,那么就会对mFirstTouchTarget进行赋值,从而不会进入2238行的条件判断,不会调用super的dispatchTouchEvent
方法,事件也就停止了传递:
如果child没有消耗事件,即child的dispatchTouchEvent
方法返回了false
,或者没有发现合适的子View,即child == null
,那么就不会给mFirstTouchTarget进行赋值,即mFirstTouchTarget == null
,通过dispatchTransformedTouchEvent
方法就会调用到super的dispatchTouchEvent
方法。
如果进行了拦截,那么直接通过2238行进而执行2240行,调用super.dispatchTouchEvent
方法,自己处理事件。
dispatchTransformedTouchEvent
的部分源码如下:
经过上述分析,可以知道:
如果不设置拦截,MyViewGroupA的直属child是MyViewGroupB,因此MyViewGroupA就会调用到MyViewGroupB的dispatchTouchEvent
方法,而MyViewGroupB的直属child是MyView,所以就会调用到View的dispatchTouchEvent
方法。
假设MyViewGroupA或者MyViewGroupB设置了事件拦截,那么就会调用ViewGroup的super.dispatchTouchEvent
,而ViewGroup继承自View,所以最终还是调用了View的dispatchTouchEvent
方法。
那么接下来就看一下View.dispatchTouchEvent
方法:
可见,上面说的onTouchEvent、OnTouchListener.onTouch
会被dispatchTouchEvent
调用,不是瞎掰的。
通过黄框圈中的代码可以很明显的看出View.dispatchTouchEvent
的返回值受onTouchEvent、OnTouchListener.onTouch
影响,并且OnTouchListener.onTouch
优先于onTouchEvent
。
源码就先看到这里,来捋一下上篇中Log_1所示的流程执行:
点击MyView,触发ACTION_DOWN
:
(1) 首先调用MyViewGroupA.dispatchTouchEvent
,在MyViewGroupA.dispatchTouchEvent
中调用MyViewGroupA.onInterceptTouchEvent
,返回false
,不拦截事件;
(2) 找到MyViewGroupA的子View,即MyViewGroupB,调用MyViewGroupB.dispatchTouchEvent
,MyViewGroupB.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群英传-事件拦截机制分析