关于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群英传-事件拦截机制分析

更多相关文章

  1. android so库、jar包生成和使用
  2. Android(安卓)LruCache源码详解
  3. 安卓巴士真诚送上营养丰富的精美Android大餐
  4. Android(安卓)multidex 使用 与 实现原理
  5. 转:教程:实现Android的不同精度的定位(基于网络和GPS)
  6. Android(安卓)GPRS的自动打开与关闭。
  7. Android在导航栏添加音量加减按钮安卓源码案例
  8. Android(安卓)开发艺术探索笔记之八 -- 理解 Window 和 WindowMa
  9. Android(安卓)中Dialog点击空白处會消失问题

随机推荐

  1. Android音频开发(7):音乐可视化-FFT频谱图
  2. Android(安卓)之 动画
  3. Android(安卓)多进程之Messenger的使用
  4. Android系统自带样式(@android:style/)
  5. 浅析Android手机卫士手机定位的原理
  6. Android音频开发(4):PCM转WAV格式音频
  7. Android(安卓)App更改背景颜色
  8. 【Android】Android(安卓)4.0 无法接收开
  9. 分享10个Android游戏源码,要的赶紧来哈
  10. Android(安卓)Trick 8: ProgressBar的圆