android关于dispatchTouchEvent和onTouchEvent的源码实验分析

(一)源码调试:设置build中编译版本为23(6.0);必须使用google官方6.0系统手机(如Nexus系列且安装6.0系统)或者使用虚拟机(配置也是Nexus6.0系统)。这样要求是为了运行设备和编译器编译使用同一个版本,而且都是google官方版本,在调试打断点的时候就不会出现断点行号和源码行号对不上的问题了。切记调试条件:

1、编译版本和测试机版本必须相同。

2、测试机使用Google官方版本,Nexus系列或者虚拟机。(使用其他品牌真机调试,因为手机框架层是被定制修改过的,所以会和编译器上的源码对不上)

(二)源码使用的是6.0系统源码。

(三)

代码结构:A继承自RelativeLayout;B继承自RelativeLayout;C继承自TextView;都重写dispatchTouchEvent和onTouchEvent两个方法并打入Log;

测试代码段:ViewGroup类dispatchTouchEvent方法。

断点代码行数:ViewGroup类,断点1:2197行,断点2:2238行。

注意:以下的验证和猜想都是针对Down动作的,不涉及move和up等。

已验证过程:假设A包含B,B包含C,所有dispatchTouchEvent和onTouchEvent返回默认值,点击C,会先调用A的dispatchTouchEvent,在断点1处停留,断点1处调用方法dispatchTransformedTouchEvent,它的参数child就是B,这个方法会使B调用自己的dispatchTouchEvent;又在断点1处停留,调用dispatchTransformedTouchEvent,它的参数child是C,方法中C会调用自己的dispatchTouchEvent;C是一个View,View的dispatchTouchEvent方法会调用自己的onTouchEvent并返回false。注意,到此,A》B》C的dispatchTouchEvent调用就完毕了,并且C执行了返回,后面就是C》B》A的onTouchEvent返回过程了,这就是个递归。继续断点,B在断点1处得到C的返回值,能够继续执行了,并且在B的断点2处停留,断点2处会再次调用B的dispatchTransformedTouchEvent方法,并且参数child为null,方法就会调用super.dispatchTouchEvent(也就是调用B的父类View的dispatchTouchEvent),然后就会调用B的onTouchEvent方法并返回false。这样一来A在断点1处有了返回值就可以继续执行了,并且在A的端点2处停留,端点2处会再次调用A的dispatchTransformedTouchEvent方法,并且参数child为null,方法会调用super.dispatchTouchEvent,方法中就调用A的onTouchEvent。至此,就完成了C》B》A的onTouchEvent回溯过程了。做一个形象的比喻:Android的触碰过程就像走楼梯,由十层一层一层的走到一层,在一层走到大厦另一边的楼梯,再由一层一层一层走到十层。默认情况下你是不可以直接由7层走到另一侧的楼梯的。

猜想一:在B的dispatchTouchEvent直接返回true,则A在断点1处停留,调用dispatchTransformedTouchEvent方法,参数child是B,B调用自己的dispatchTouchEvent并直接返回true。则A在断点1处有了返回true可以继续执行,然后就在A的断点2处停留,最后会调用到A的onTouchEvent

猜想一结果:真实的运行结果是执行了A的dispatchTouchEvent和B的dispatchTouchEvent就结束了,并没有执行A的onTouchEvent。

猜想一分析:猜想和结果的出入就是最后是否调用了A的onTouchEvent,看断点1处的代码:

A执行dispatchTransformedTouchEvent,也就是想B分发,执行B的dispatchTouchEvent,根据上面的猜想B的dispatchTouchEvent会直接返回true,if成立,然后就会执行2213和2214行,2214行给标志位alreadyDispatchedToNewTouchTarget置为true,2213行执行addTouchTarget方法并把返回值置给newTouchTarget:

在addTouchTarget方法中给mFirstTouchTarget赋值并把一个相同值返回付给了newTouchTarget。

再回到流程中看,B的dispatchTouchEvent直接返回true,A的dispatchTransformedTouchEvent有了返回值就可以继续执行代码,执行到断点2处又碰到一个if判断:

由断点1处分析可知,mFirstTouchTarget等于newTouchTarget且不等于null,所有if不成立(跳过了回溯过程中调用dispatchTransformedTouchEvent的第一次机会),执行2243-2270行,其中2249行又遇到一个if判断,判断条件alreadyDispatchedToNewTouchTarget等于true、target也的确等于newTouchTarget(见前面),if成立(跳过了回溯过程中调用dispatchTransformedTouchEvent的第二次机会)。在dispatchTouchEvent直接返回true而导致的后续过程中我们可以看到,我们根本没有机会运行到dispatchTransformedTouchEvent方法,也就没有机会执行到onTouchEvent方法。

猜想二:在B的onTouchEvent直接返回true,则完整执行dispatchTouchEvent的A》B》C过程,onTouchEvent方法的回溯过程只执行到B就结束。

猜想二结果:点击C,执行过程:A的dispatchTouchEvent》B的dispatchTouchEvent》C的dispatchTouchEvent》C的onTouchEvent》B的onTouchEvent结束。与猜想一致。

下面是对UP事件的分析:

在猜想二验证结果时,程序的log除了显示上面的“猜想二结果”,还显示了一套UP事件的传递log:A的dispatchTouchEvent》B的dispatchTouchEvent》B的onTouchEvent结束。

也就是说目标是要从三楼左侧的楼梯去到二楼右侧的楼梯。D君(Down事件)先从三楼左侧楼梯一直下到一楼,穿过一楼楼层到达一楼右侧楼梯,在爬到二楼右侧楼梯,到达!U君(UP事件)从三楼左侧楼梯下到二楼,然后直接穿过二楼楼层到达二楼右侧楼梯,到达!!

源码上说up流程的成因只需要明白一点:down流程之所以会有dispatchTouchEvent下发和onTouchEvent回溯两个对称流程,是因为代码层次上有断点1和断点2两个地方能够执行两次dispatch方法(第一次目的是为了能够调用dispatchTouchEvent,第二次目的是为了能够调用onTouchEvent)。而对于up流程,在源码中断点1是包含在一个if语句中(2143-2145):

if(actionMasked == MotionEvent.ACTION_DOWN || (split && actionMasked == MotionEvent.ACTION_POINTER_DOWN) || actionMasked == MotionEvent.ACTION_HOVER_MOVE)

可以看到,只有down和move事件能够执行断点1处的dispatch,所以up就只能执行一次断点2处的dispatch了,紧接着我们再来看一遍断点2代码段:

仔细看,无论if是否成立,都会调用dispatchTransformedTouchEvent方法,不同的是方法的第三个参数child不同,下面直接给出结论:当mFirstTouchTarget为null是dispatchTransformedTouchEvent的child传null,则调用当前类的onTouchEvent方法;反之,child传子View,则调用子View的dispatchTouchEvent方法。

这个结论的关键点是mFirstTouchTarget是否为null,当它不为null时dispatchTouchEvent继续下放,当它为null时调用同级的onTouchEvent并开始回溯。

那mFirstTouchTarget是在哪里设置的呢?mFirstTouchTarget只有在down过程中才会被设置,具体参考猜想一中的分析。我们可以想象得到,在down过程中,当B的onTouchEvent返回true,调用它的A中mFirstTouchTarget就会被赋值(A的父集们的mFirstTouchTarget都会递归被赋值),而B中的mFirstTouchTarget还是保持为null。有了这个结果,在up过程中到了B的断点2处,mFirstTouchTarget为null,就会调用同级的onTouchEvent,这样就实现了从三楼左侧楼梯下到二楼直接穿过楼层到达二楼右侧楼梯。

没有实践调试的过程,看起来绝对是似是而非似懂非懂的,所以Debug源码才是最终理解的正途!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 195,980评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,422评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,130评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,553评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,408评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,326评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,720评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,373评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,678评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,722评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,486评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,335评论 3 313
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,738评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,009评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,283评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,692评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,893评论 2 335

推荐阅读更多精彩内容