Android的事件分发

什么是事件?

事件指的是点击事件, 也即用户从触摸屏幕到离开屏幕的过程. Android将事件分为了下面四种事件:

  1. DOWN事件: 指用户按下View的事件, 是一切事件的开始.

  2. MOVE事件: 用户滑动View的事件

  3. UP事件: 用户离开View的事件

  4. CANCEL事件: 非人为原因致使事件结束

一个完整的事件应该包括一个DOWN事件, 一个UP事件以及若干个二者之间的MOVE事件.

事件的分发流程

我们知道, Android的UI由Activity、ViewGroup(及其派生类)、View(及其派生类)组成. 事件也正是向这三类组件进行分发. 分发的顺序就是三者的包含顺序: Activity、ViewGroup、View. 这三个层级的事件分发流程如下图所示:

在这张图中, 什么都不标的箭头表示方法的调用, 标着true或者false的箭头表示根据什么都不标的箭头表示的方法的结果来调用的方法. 可以看到, 事件分发涉及的主要方法就三个, 下面我们以此来简单地讲解这三个方法.

dispatchTouchEvent

首先说一下返回值. 对于Activity来说, 方法的返回值就是下一级的dispatchTouchEvent的返回值; 对于ViewGroup来说, 方法的返回值取决于ViewGroup是否允许拦截事件, 是的话返回值就是ViewGroup的onTouchEvent方法的返回值, 否则就是下一级控件的dispatchTouchEvent的返回值(当然, 这里的下一级控件既可以是View, 也可以是ViewGroup, 套娃嘛); 对于View来说, 方法的返回值是这个View的onTouchEvent返回值.

然后我们说一下作用对象. 对于Activity来说, 它只能直接接触最外层的ViewGroup, 所以就无脑问这个ViewGroup就行了; 对于ViewGroup来说, 它可能有无数个View或者ViewGroup作为子控件, 那么它就得遍历所有子控件来找到应该将事件分发给哪个控件(判断方法很简单, 看事件的位置在哪个控件的矩形区域内); 对于View来说, 它不能再向下分发了, 因此作用对象就是自己.

onInterceptTouchEvent

这个方法是专门属于ViewGroup的. 如果ViewGroup允许拦截事件, 那么它就不向子控件下发事件, 而是直接执行自己的onTouchEvent.

onTouchEvent

先说返回值. 这个方法的返回值就是控件有没有处理这个事件, 处理了就是true, 否则就是false.

再说工作流程. onTouchEvent其实也是一系列流程. 源码的逻辑很好懂, 大意就是根据事件的状态进行一个判断. 比如上一个事件的DOWN事件, 这次的事件是一个UP事件, 那就根据两次事件的时间差决定是调用onClick还是onLongClick.

那么问题来了, 如果没有收到DOWN事件呢?

首先, 这种情况是存在的. 我们可以重写dispatchTouchEvent, 让某一个ViewGroup只下发MOVE事件和UP事件. 这样就会出现问题: 对于ViewGroup来说, 它只能收到DOWN事件; 对于它的子控件来说, 子控件只能收到MOVE和UP事件. 显然, 这两种情况都不能构成一个完整的事件列.

为了解决这个问题, onTouchEvent又加了一条约束: 除非该控件上一个处理的事件是DOWN事件, 否则不处理MOVE事件和UP事件. 这样一来, 即使例子中的ViewGroup把事件分发了下去, 子控件也不会处理这些事件. 最终, 这些事件还是会被ViewGroup来处理.