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

在上一篇文章 Android事件分发机制一:事件是如何到达activity的? 中,我们讨论了触摸信息从屏幕产生到发送给具体 的view处理的整体流程,这里先来简单回顾一下:

整体流程

触摸信息从手机触摸屏幕时产生,通过IMS和WMS发送到viewRootImpl

viewRootImpl把触摸信息传递给他所管理的view

view根据自身的逻辑对事件进行分发

常见的如Activity布局的顶层viewGroup为DecorView,他对事件分发方法进行了重新,会优先回调windowCallBack也就是Activity的分发方法

最后事件都会交给viewGroup去分发给子view

前面的分发步骤我们清楚了,那么viewGroup是如何对触摸事件进行分发的呢?View又是如何处理触摸信息的呢?正是本文要讨论的内容。

事件处理中涉及到的关键方法就是 dispatchTouchEvent ,不管是viewGroup还是view。在viewGroup中,dispatchTouchEvent 方法主要是把事件分发给子view,而在view中,dispatchTouchEvent 主要是处理消费事件。而主要的消费事件内容是在 onTouchEvent 方法中。下面讨论的是viewGroup与view的默认实现,而在自定义view中,通常会重写 dispatchTouchEvent 和 onTouchEvent 方法,例如DecorView等。

秉着逻辑先行源码后到的原则,本文虽然涉及到大量的源码,但会优先讲清楚流程,有时间的读者仍然建议阅读完整源码。

理解MotionEvent

事件分发中涉及到一个很重要的点:多点触控,这是在很多的文章中没有体现出来的。而要理解viewGroup如何处理多点触控,首先需要对触摸事件信息类:MotionEvent,有一定的认识。MotionEvent中承载了触摸事件的很多信息,理解它更有利于我们理解viewGroup的分发逻辑。所以,首先需要先理解MotionEvent。

触摸事件的基本类型有三种:

ACTION_DOWN: 表示手指按下屏幕

ACTION_MOVE: 手指在屏幕上滑动时,会产生一系列的MOVE事件

ACTION_UP: 手指抬起,离开屏幕

一个完整的触摸事件系列是:从ACTION_DOWN开始,到ACTION_UP结束 。这其实很好理解,就是手指按下开始,手指抬起结束。

手指可能会在屏幕上滑动,那么中间会有大量的ACTION_MOVE事件,例如:ACTION_DOWN、ACTION_MOVE、ACTION_MOVE...、ACTION_UP。

这是正常的情况,而如果出现了一些异常的情况,事件序列被中断,那么会产生一个取消事件:

ACTION_CANCEL:当出现异常情况事件序列被中断,会产生该类型事件

所以,完整的事件序列是:从ACTION_DOWN开始,到ACTION_UP或者ACTION_CANCEL结束 。当然,这是我们一个手指的情况,那么在多指操作的情况是怎么样的呢?这里需要引入另外的事件类型:

ACTION_POINTER_DOWN: 当已经有一个手指按下的情况下,另一个手指按下会产生该事件

ACTION_POINTER_UP: 多个手指同时按下的情况下,抬起其中一个手指会产生该事件

区别于ACTION_DOWN和ACTION_UP,使用另外两个事件类型来表示手指的按下与抬起,使得ACTION_DOWN和ACTION_UP可以作为一个完整的事件序列的边界

同时,一个手指的事件序列,是从ACTION_DOWN/ACTION_POINTER_DOWN开始,到ACTION_UP/ACTION_POINTER_UP/ACTION_CANCEL结束。

到这里先简单做个小结:

触摸事件的类型有:ACTION_DOWN、ACTION_MOVE、ACTION_UP、ACTION_POINTER_DOWN、ACTION_POINTER_UP,他们分别代表不同的场景。

一个完整的事件序列是从ACTION_DOWN开始,到ACTION_UP或者ACTION_CANCEL结束。
一个手指的完整序列是从ACTION_DOWN/ACTION_POINTER_DOWN开始,到ACTION_UP/ACTION_POINTER_UP/ACTION_CANCEL结束。

第二,我们需要理解MotionEvent中所携带的信息。

假如现在屏幕上有两个手指按下,如下图:

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

触摸点a先按下,而触摸点b按下,那么自然而然就会产生两个事件:ACTION_DOWN和ACTION_POINTER_DOWN。那么是不是ACTION_DOWN事件就只包含有触摸点a的信息,而ACTION_POINTER_DOWN只包含触摸点b的信息呢?换句话说,这两个事件是不是会独立发出触摸事件?答案是:不是。

每一个触摸事件中,都包含有所有触控点的信息。例如上述的点b按下时产生的ACTION_POINTER_DOWN事件中,就包含了触摸点a和触摸点b的信息。那么他是如何区分这两个点的信息?我们又是如何知道ACTION_POINTER_DOWN这个事件类型是属于触摸点a还是触摸点b?

在MotionEvent对象内部,维护有一个数组。这个数组中的每一项对应不同的触摸点的信息,如下图:

image.png

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

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