首页 > 代码库 > Android好奇宝宝_08_乱侃触摸事件传递
Android好奇宝宝_08_乱侃触摸事件传递
讲解触摸事件传递原理的网上有一大把,有从源码角度讲的,有从实际例子角度讲的。我这里呢只是记录下自己的理解,讲的可能没其他大牛透彻,有错误的跪求评论指正。
直接上图,对照图解和文字来分析触摸事件的传递。
(1)乱七八糟图解版:
(2)看图说话:
(2.1)上图并不包含Activity,Activity没有onInterceptTouchEvent方法,默认实现都是直接往下传递。
(2.2)讲解3个触摸事件控制方法的含义及其返回值的意义:
<1>onInterceptTouchEvent
onInterceptTouchEvent是ViewGroup特有的方法,Activity和View并没有此方法,它的作用是用于拦截触摸事件不再传递给子View(Activity和View并没有子View,所以没有此方法很符合逻辑),默认实现不拦截。如果拦截的话子View将完全接受不到触摸事件,子View的任何相关触摸事件的方法都不会被调用。(注:有例外情况我会在后面再说)(有特殊情况)
返回true:
表示拦截触摸事件,不继续调用子View的dispatchTouchEvent方法,直接调用自己的onTouchEvent方法。(有特殊情况)
返回false:
表示不拦截事件,继续去调用子View的dispatchTouchEvent。
<2>dispatchTouchEvent
用于分派触摸事件,注意是分派事件,而不是处理事件,这个方法应该做的是决定事件的传递方向,而不是处理消费掉触摸事件。
返回true:
表示自己要消费这个事件,但并不是马上消费,而是如果其子View没人消费的话,则自己消费。事件还是会继续往下传递的,只是在往上传递过程中如果还能到达自己的话,那么就会去消费这个事件。
返回false:
表示自己不消费这个事件,如果同时onTouchEvent也返回false表示不消费这个事件的话,就会继续往上传递给parent的onTouchEvent。
<3>onTouchEvent
用于处理事件,如果你想处理触摸事件并做出相应的动作,那么应该在这个方法中进行。
返回true:
表示自己想消费这个事件,并且马上生效,即刻消费,事件传递停止。
返回false:
表示自己不想消费这个事件,同时检查dispatchTouchEvent之前若是返回了true,就会发现:哎呀,我之前说子View没人处理的话,那么就我来处理。好吧,我要遵守诺言,就交给我处理吧,不要再传递了。
注意dispatchTouchEvent和onTouchEvent返回true的区别:
dispatchTouchEvent是没人消费的话就交给我消费,你们都没人要啊,那就给我吧,各位大爷真是好人啊。
onTouchEvent是我现在就要消费,其它人都没份了,都干嘛干嘛去,就是这么霸气,就是这么任性。
(2.3)传递过程中那些可能发生的事
触摸事件的第一个接收者总是Activity,Activity一般不会去消费触摸事件,而是直接传递给处于该触摸事件范围的View树集合。在View树中等级位置对应在触摸事件中的位置,即越外层的View,就位于越顶层,而其child则位于底层。即Activity在最顶层,然后接下来是容器控件,最后的View在最底层。
在一般情况下:
事件先从上往下,经过每一个dispatchTouchEvent。到达最后一个View时,调用其onTouchEvent,开始从下往上的传递过程,经过每一个onTouchEvent,这是没任何View想要消费触摸事件的情况。
可能发生的几个转折点:
<1>在从上往下的过程中,如果某个View的onInterceptTouchEvent返回了true,表示拦截事件,事件不会继续往下传递,而是直接调用这个View的onTouchEvent,注意这并不意味着这个View消费了这个事件,只是拦截了这个事件继续往下传递,剥夺了子View的消费权,提前开始从下往上的过程而已,是否由它消费还要看其onTouchEvent的返回值是否为true。(有特殊情况)
<2>在从下往上的过程中,如果某个View的onTouchEvent返回了true,表示自己想要消费这个事件,事件不会继续往上传递,事件传递结束。
<3>还是在从下往上的过程中,如果某个View在之前的dispatchTouchEvent方法返回了true,表示在之前这个View表明如果所有子View都不消费这个事件的话,那么就让我来消费,事件不会继续往上传递,事件传递结束。
(所有子View都不消费这个事件意味着所有子View的dispatchTouchEvent和onTouchEvent都返回了false,不然事件早被消费且传递结束了,根本不会再到达自己。)
<4>这个比较少用,某个子View调用了parent的requestDisallowInterceptTouchEvent方法,表示请求parent不要拦截事件,这种情况下parent的onInterceptTouchEvent返回了true也拦截不了事件,即让onInterceptTouchEvent方法失效,这就是上面3个标记了(有特殊情况)所说的特殊情况。
(2.4)记忆功能
记忆功能是指,如果某个View消费了Down事件,那么接下来的Move和Up等事件还是会从Activity顶层向下传递,但是传递到这个View时,即使这个View并没有用onInterceptTouchEvent方法来拦截事件,事件还是会被拦截,直接由该View处理。即如果一个View消费了Down事件,则接下来的Move和Up事件也会交给它消费。
这么做的原因是:一般情况下单一的触摸事件并不能形成有效的动作。比如一个拖动动作需要Down事件和连续的Move事件,点击动作至少需要一个Down事件和Up事件。把单一的触摸事件分派给不同的View,往往形成不了有意义的动作,所以加入了这个记忆功能来减少事件的传递。
(3)随便说点
理解触摸事件的传递一般是为了解决触摸事件发生冲突的情况,处理触摸事件冲突时要注意一点:
我们要处理的是由谁来处理触摸事件,而不是去修改消费这个事件的View应该去怎么处理。
有点拗口是吧,再说白一点就是我们应该控制谁来处理,而不是控制怎么去处理,每个View都有自己的处理逻辑,我们不应该去修改它。
举个栗子:
手指左右拖动不了ViewPager时,我们想得应该是怎么在手指左右拖动的情况下让ViewPager能获得触摸事件的消费权,而不要管接受到触摸事件后怎么让ViewPager随着手指滚动,这个功能ViewPager自己已经实现了,你把触摸事件给它,它就能滚。
好了,本篇完结,建议对触摸事件传递机制不熟悉的自己写个例子打印些Log日志验证一下,再百度谷歌下一下常见的触摸冲突及其解决办法。
求赞求评论求指点啊!!!
Android好奇宝宝_08_乱侃触摸事件传递