Android 事件分发机制解析

前言

正文

1. 事件分发机制实际上是 MotionEvent ( 点击事件 ) 的传递规则。

2. 事件分发机制涉及的三个方法

image

3. View 事件的传递流程

image

  • View 没有 onInterceptTouchEvent 方法,一旦有点击事件传递给它,那么它的 onTouchEvent 方法就会被调用

  • View 的 onTouchEvent 方法默认都会消耗事件(返回 true),除非是不可点击的( clickable 和 longClickable 同时为 false),View 的 longClickable 属性默认都为 false ,而 clickable 属性分情况,比如 Button 默认为 true ,TextView 默认为 false

  • View 的 enable 属性不影响 onTouchEvent 方法的返回值,哪怕是 disable 状态,只要 clickable 或者 longClickable 有一个为 true ,那么它的 onTouchEvent 就返回 true。

4. ViewGroup 事件的传递流程

image

5. 一些注意事项

1. 所谓的点击事件,是指从 down 事件开始,中间可能含有数量不定的 move 事件,最终以 up 事件结束的一系列事件,称为:同一个事件序列
2. 某个 View 一旦决定拦截事件,那么这一个事件序列都有它来处理。
3. 某个 View 一旦开始处理事件,如果它不消耗 ACTION_DOWN 事件( onTouchEvent ),那么同一事件序列中的其他事件都不会再交给它来处理,并且事件将重新交给它的父元素去处理,即父元素的 onTouchEvent 会被调用。
4. 事件的传递过程都是由外向内的。
5. onClick 发生的前提是当前 View 是可点击的,并且收到了 down 和 up 事件。

6. actvity 对点击事件的分发过程

1. 当一个点击操作发生时,事件最先传递给 Activity ,由 Activity 的 dispatchTouchEvent 来进行事件的分发,具体工作是由 Activity 内部的 Window 来完成的,Window 会将事件传递给 DecorView ,然后将事件传递给 Activity 的顶级 View ,也就是设置的 setContentView() ;
1
2
3
4
5
6
7
8
9
10
// Activity 的 源码 dispatchTouchEvent 方法
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
2.Window 是个抽象类,其 superDispatchTouchEvent 方法也是抽象方法,
1
2
3
4
5
6
7
// Window 类源码
public abstract class Window {
/** Flag for the "options panel" feature. This is enabled by default. */
public static final int FEATURE_OPTIONS_PANEL = 0;
/** Flag for the "no title" feature, turning off the title at the top
* of the screen. */
public static final int FEATURE_NO_TITLE = 1;

在 Activity 源码中有这样一行代码:

1
mWindow = new PhoneWindow(this, window)

从而可以知道 Window 类的实现类为 PhoneWindow ,而实际上 PhoneWindow 是其唯一的一个实现类

Android SDK 隐藏了 PhoneWindow 类源码,本人只好从网上找了源码,看 superDispatchTouchEvent 方法

1
2
3
4
5
6
public class PhoneWindow extends Window implements MenuBuilder.Callback {
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}

从这里可以看出 PhoneWindow 将事件传递给了 mDecor,也就是 DecorView ,其类如下:

1
private final class DecorView extends FrameLayout {

实际上 DecorView 是一个 FrameLayout ,而我们通过 setContentView 设置的 View 就是其一个子类。

然后,DecorView 就会将事件传递给 Activity 设置的 View,也就是顶级 View

接下来就是 View 对点击事件的分发过程了。

7. View 对点击事件的分发过程

之前的 第 2 条、第 3 条和第 4 条 都讲过了,下边再叙述几点:

点击事件到达顶级 View 以后 (一般是一个 ViewGroup )

  • 如果顶级 ViewGroup 拦截事件的情况下,设置了 OnTouchListener ,则 onTouch 会被调用,反之会调用 onTouchEvent ,也就是说,如果都提供的话,onTouch 会屏蔽掉 onTouchEvent 。OnTouchListener 的优先级高于 onTouchEvent