Android ViewGroup中事件触发和传递机制
针对由于触摸(Touch)而触发的事件。
的事件:, , 等等,都是由许多个Touch组成的。其中Touch的第一个状态肯定是 , 表示按下了屏幕。之后,touch将会有后续事件,可能是:
//表示为移动手势
//表示为离开屏幕
//表示取消手势,不会由用户产生,而是由程序产生的
一个, n个, 1个,就构成了中众多的事件。
在中,有一类控件是中还可以包含其他的子控件,这类控件是继承于类,例如:, , 。
还有一类控件是不能再包含子控件,例如:。
本文的主要讨论对象就是类的控件嵌套时事件触发情况。
对于类的控件,有一个很重要的方法,就是t(),用于处理事件并改变事件的传递方向,它的返回值是一个布尔值,决定了Touch事件是否要向它包含的子View继续传递,这个方法是从父View向子View传递。
而方法(),用于接收事件并处理,它的返回值也是一个布尔值,决定了事件及后续事件是否继续向上传递,这个方法是从子View向父View传递。
Touch事件在 t()和以及各个间的传递机制完全取决于t()和()的返回值。返回值为true表示事件被正确接收和处理了,返回值为false表示事件没有被处理,将继续传递下去(只是传递方向不一样,t()向子View传,而()向父View传)。
具体情况如下:
事件会传到某个类的t,如果返回false,则DOWN事件继续向子类的t传递,如果子View不是类的控件,则传递给子View的。
如果t返回了true,则DOWN事件传递给的,不再继续传递,并且之后的后续事件也都传递给它的。
如果某View的返回了false,则DOWN事件继续向其父类的传递;如果返回了true,则后续事件会直接传递给其继续处理。(后续事件只会传递给对于必要事件返回了true的)
/`````````````````````````````````````````````````````````````````
以前写,对事件的处理没有太深入,只是简单的就ok了,现在写的UI,很多自定义组件,父view和子view都需要接收事件,然后处理。如果不弄明白它的事件传递机制,很难拥有好的用户体验。
中,返回值是true,则说明消耗掉了这个事件,返回值是false,则没有消耗掉,会继续传递下去,这个是最基本的。
在View中跟Touch相关的事件有,,三种。是负责分发事件的,事件从传递出来之后,最先到达的就是最顶层view的,然后它进行分发,如果返回false,则交给这个view的方法来决定是否要拦截这个事件,如果返回true,也就是拦截掉了,则交给它的来处理,如果返回false,那么就传递给子view,由子view的再来开始这个事件的分发。
如果事件传递到某一层的子view的上了,这个方法返回了false,那么这个事件会从这个view往上传递,都是来接收。而如果传递到最上面的也返回false的话,这个事件就会“消失”,而且接收不到下一次事件。(我说的一次事件指的是down到up之间的一系列事件)
我画了个图,见附件。
总结一下,如果这一次事件没有人消耗掉,则系统不会给你下一次事件,因为他会认为你这次的事件阻塞了,没必要给下一次。如果不消耗的话,会从子view传递到父view。
又一个例子:
需求:要做一个完全通过flip手势来切换的界面。在最上层用一个作为容器,并检测flip手势操作。
难题:的flip手势检测需要的会被各种子View的触摸检测给拦截了。比如界面上有一个,则当手指按下(还没有抬起)然后flip出,则最上层的flip手势检测无效。
原因:对Touch Event的分发逻辑是View从上层分发到下层(函数),然后下层优先开始处理Event(先,再)并向上返回处理情况(值),若返回true,则上层不再处理。
于是我们首先想到,要保证flip手势检测,需要把所有的Touch Event都传到上层去。
然而在分发逻辑之外还有一个逻辑,估计是为了保证每个触操作只能由一个View来进行完整响应,对事件有个额外的逻辑:如果某个View在处理事件时返回false(即该View未处理此事件),那么后续产生的其它事件将直接忽略掉这个View(不过又有另外的独立逻辑)。举例来说就是,如果你处理时返回了false,那么你这个View将得不到或等等这些后续事件了。
于是难题出现了,你若把Touch Event都想办法给传到上层了(只能通过返回false来传到上层),那么下层的各种子View就不能处理后续事件了。
解决方案:
开始仅着眼于Touch Event处理完后的回传过程,想了N久不得,毕竟我想实现的是一个需要打破事件处理逻辑的效果(就是一个连续性操作,只有不满足上层要求时,才轮到下层处理)。然后突然想到事件的分发过程,便豁然开朗:
覆写最上层的View的函数,代码如下:
@
( event) {
if (.(event)) {
event.(.);
super.(event);
于是效果实现。也就是在分发之前便进行手势检测处理,若检测成功,则取消下层的一切处理过程。
总结一下就是:t可以接受到所有的Touch事件,而则不一定。