Android事件分发机制二:viewGroup与view对事件的处理 (2)

数组下标称为触控点的索引,每个节点,拥有一个触控点的完整信息。这里要注意的是,一个触控点的索引并不是一成不变的,而是会随着触控点的数目变化而变化。例如当同时按下两个手指时,数组情况如下图:

image.png

而当手指a抬起后,数组的情况变为下图:

image.png

可以看到触控点b的索引改变了。所以跟踪一个触控点必须是依靠一个触控点的id,而不是他的索引

现在我们知道每一个MotionEvent内部都维护有所有触控点的信息,那么我们怎么知道这个事件是对应哪个触控点呢?这就需要看到MotionEvent的一个方法:getAction 。

这个方法返回一个整型变量,他的低1-8位表示该事件的类型,高9-16位表示触控点索引。我们只需要将这16位进行分离,就可以知道触控点的类型和所对应的触控点。同时,MotionEvent有两个获取触控点坐标的方法:getX()/getY() ,他们都需要传入一个触控点索引来表示获取哪个触控点的坐标信息。

同时还要注意的是,MOVE事件和CANCEL事件是没有包含触控点索引的,只有DOWN类型和UP类型的事件才包含触控点索引。这里是因为非DOWN/UP事件,不涉及到触控点的增加与删除。

这里我们再来小结一下:

一个MotionEvent对象内部使用一个数组来维护所有触控点的信息

UP/DOWN类型的事件包含了触控点索引,可以根据该索引做出对应的操作

触控点的索引是变化的,不能作为跟踪的依据,而必须依据触控点id

关于MotionEvent需要了解一个更加重要的点:事件分离。

首先需要知道事件分发的一个原则:一个view消费了某一个触点的down事件后,该触点事件序列的后续事件,都由该view消费 。这也比较符合我们的操作习惯。当我们按下一个控件后,只要我们的手指一直没有离开屏幕,那么我们希望这个手指滑动的信息都交给这个view来处理。换句话说,一个触控点的事件序列,只能给一个view消费。

经过前面的描述我们知道,一个事件是包含所有触摸点的信息的。当viewGroup在派发事件时,每个触摸点的信息就需要分开分别发送给感兴趣的view,这就是事件分离。

例如Button1接收了触摸点a的down事件,Button2接收了触摸点b的down事件,那么当一个MotionEvent对象到来时,需要将他里面的触摸点信息,把触摸点a的信息拆开发送给button1,把触摸点b的信息拆开发送给button2。如下图:

事件分离

那么,可不可以不进行分离?当然可以。这样的话每次都把所有触控点的信息发送给子view。这可以通过FLAG_SPLIT_MOTION_EVENTS这个标志进行设置是否要进行分离。

小结一下:

一个触控点的序列一般情况下只给一个view处理,当一个view消费了一个触控点的down事件后,该触控点的事件序列后续事件都会交给他处理。

事件分离是把一个motionEvent中的触控点信息进行分离,只向子view发送其感兴趣的触控点信息。

我们可以通过设置FLAG_SPLIT_MOTION_EVENTS标志让viewGroup是否对事件进行分离

到这里关于MotionEvent的内容就讲得差不多,当然在分离的时候,还需要进行一定的调整,例如坐标轴的更改、事件类型的更改等等,放在后面讲,接下来看看ViewGroup是如何分发事件的。

ViewGroup对于事件的分发

这一步可以说是事件分发中的重头戏了。不过在理解了上面的MotionEvent之后,对于ViewGroup的分发细节也就容易理解了。

整体来说,ViewGroup分发事件分为三个大部分,后面的内容也会围绕着三大部分展开:

拦截事件:在一定情况下,viewGroup有权利选择拦截事件或者交给子view处理

寻找接收事件序列的控件:每一个需要分发给子view的down事件都会先寻找是否有适合的子view,让子view来消费整个事件序列

派发事件:把事件分发到感兴趣的子view中或自己处理

大体的流程是:每一个事件viewGroup会先判断是否要拦截,如果是down事件(这里的down事件表示ACTION_DOWN和ACTION_POINTER_DOWN,下同),还需要挨个遍历子view看看是否有子view消费了down事件,最后再把事件派发下去。

在开始解析之前,必须先了解一个关键对象:TouchTarget。

TouchTarget

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wpzxwf.html