1. 形式

Touch 事件被封装成 MotionEvent 对象来传递。
由 ACTION_DOWN 开始,经过若干次 ACTION_MOVE 并以 ACTION_UP 结束的一个事件序列称为一个 gesture。

2. 角色

涉及 Touch 事件处理的角色有四种:

  • Activity
  • Window
  • ViewGroup
  • View

3. 基本流程

事件在 activity 中的分发起始于 dispatchTouchEvent() 方法。从源码可以看出 activity 调用了 Window 的 superDispatchTouchEvent() 方法来处理,若返回值为 true,则结束事件分发,否则调用 onTouchEvent() 方法。

1
2
3
4
5
6
7
8
9
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}

Window 的实现类为 PhoneWindow,PhoneWindow 实际调用了 DecorView 的 superDispatchTouchEvent()
而 DecorView 是 activity 中的顶级 View,它其实是一个 FrameLayout,它的 superDispatchTouchEvent() 直接调用了 ViewGroup 的 dispatchTouchEvent()

由此,事件流入 View 层级。

1
2
3
4
5
6
7
8
9
// PhoneWindow
@Override
public boolean superDispatchTouchEvent(MotionEvent event) {
return mDecor.superDispatchTouchEvent(event);
}
// DecorView
public boolean superDispatchTouchEvent(MotionEvent event) {
return super.dispatchTouchEvent(event);
}

4. ViewGroup dispatchTouchEvent() 流程

事件流入 View 层级后会逐级分发,ViewGroup 处在事件分发的中间层,是事件分发机制中最复杂的部分。此处源码非常长,就不贴出来了。

ACTION_DOWN 事件作为一次 gesture 的开始,可以影响后续事件的传递流程。为了方便理解,这里将它与其他类型的事件分开分析。

  • 对于 ACITON_DOWN 事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;

// 1. 作为一次 gesture 的开始,重置状态

// 2. 判断是否需要拦截本次事件
final boolean intercepted;
if (!disallowIntercept) {
// 此时 disallowIntercept 已被重置为 false,onInterceptTouchEvent 一定会被调用
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
intercepted = false;
}

if (!intercepted && mChildrenCount != 0) {
// 3.a 不进行拦截的情况,向下分发
// 从前面到后面遍历子 View,这里用循环仅简单表示遍历过程
for (View child : mChildren) {
if (!canViewReceivePointerEvents(child)
|| !isTransformedTouchPointInView(x, y, child, null)) {
// 该子 View 不可见或触摸位置不在子 View 边界内,跳过
continue;
}
// 变换 MotionEvent 并传给子 View 的 dispatchTouchEvent
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
// 若返回 true,表示该子 View 需要处理此次 gesture 的事件,记录此状态
handled = true;
}
}
} else {
// 3.b 进行拦截的情况,需要自己处理事件
handled = super.dispatchTouchEvent(event);
}

return handled;
}
  • 对于非 ACITON_DOWN 事件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
boolean handled = false;

// 1. 判断是否需要拦截本次事件
final boolean intercepted;
if (!disallowIntercept) {
intercepted = onInterceptTouchEvent(ev);
ev.setAction(action); // restore action in case it was changed
} else {
// 若之前子 View 调用了 requestDisallowInterceptTouchEvent(true),则不允许拦截
intercepted = false;
}

if (!intercepted && mChildrenCount != 0) {
// 2.a 不进行拦截的情况,向下分发
// 仅遍历之前记录的子 View(即对 ACTION_DOWN 事件返回 true 的子 View)
// 这里循环仅简单表示遍历过程,实际上源码是使用链表实现的
for (View child : children) {
// 变换 MotionEvent 并传给子 View 的 dispatchTouchEvent
if (dispatchTransformedTouchEvent(ev, false, child, idBitsToAssign)) {
handled = true;
}
}
} else {
// 2.b 进行拦截的情况,需要自己处理事件
handled = super.dispatchTouchEvent(event);
}

return handled;
}

5. View dispatchTouchEvent() 流程

View 位于事件分发的最下层,不再向下分发。View 的事件分发逻辑大致等效于 return mOnTouchListener.onTouch(this, e) || onTouchEvent(e);

View 的 OnClickListener 是在 onTouchEvent() 方法里回调的,所以如果我们为 View 设置了 onTouchListener 并在 onTouch() 里返回 true,那么它的 OnClick 等系统预设的事件会失效。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 简化的 View dispatchTouchEvent 代码
public boolean dispatchTouchEvent(MotionEvent event) {
boolean result = false;

ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null
&& (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
result = true;
}

if (!result && onTouchEvent(event)) {
result = true;
}

return result;
}