(一)基本概念
①、概述: Android里的View都是树形结构的,因此可能会出现多个View重叠的情况。当我们点击或滑动屏幕上重叠的部分的时候,这些View默认都可以响应。如果此时我们只想要某个View处理(响应/消费)这个事件,这时就可以用事件分发机制去解决这个问题。
需要说明的是,Android的事件分发机制是一种典型的责任链模式,可参考责任链模式详解。
②、事件的分发、拦截、消费(响应)
流程 | 对应方法 | Activity | ViewGroup | View |
---|---|---|---|---|
分发 | dispatchTouchEvent | 支持 | 支持 | 支持 |
拦截 | onInterceptTouchEvent | 不支持 | 支持 | 不支持 |
消费 | onTouchEvent | 支持 | 支持 | 支持 |
③、方法说明:
Android中触摸事件传递过程中最重要的是dispatchTouchEvent()、onInterceptTouchEvent()和onTouchEvent()方法。
其中:dispatchTouchEvent 是事件分发机制中的核心,所有的事件(包括点击、长按、触摸等)调度都归它管。
1 | // 事件分发。一般不用重写。 |
④、Touch触摸事件:
Android中的Touch事件都是从ACTION_DOWN开始的:
单手指操作:ACTION_DOWN---ACTION_MOVE----ACTION_UP
多手指操作:ACTION_DOWN---ACTION_POINTER_DOWN---ACTION_MOVE--ACTION_POINTER_UP---ACTION_UP.
事件的调度顺序是:onTouchListener > onTouchEvent > onLongClickListener > onClickListener
需要特别指出的是:如果两个View(View1、View2)有重叠且View2(在上层)遮挡了View1的部分,则当手指点击时:
1> 仅当View1可点击时,事件将分配给View1,被View2遮挡的部分仍是View1可点击的区域。
2> 仅当View2可点击时,事件将分配给View2。
3> 当View1、View2都可以点击时,事件将分配给View2,View1收不到事件。
⑤、事件总结:
1、onTouchEvent包含了onClickListener和onLongClickListener事件。
2、View的dispatchTouchEvent主要用于调度自身的监听器和onTouchEvent。
3、给View注册OnTouchListener监听不会影响View的可点击状态,只要View是可点击的就会消费事件。
4、若给ViewGroup及其ChildView同时注册了事件监听器(如:onClick),则ChildView会被消费,ViewGroup收不到事件(无响应)。
5、如果当前正在处理的事件被上层View拦截,会收到一个ACTION_CANCEL,后续事件不会再传递过来。
6、为避免一次完整的事件(如:onClick包含ACTION_DOWN、ACTION_UP)分配给不同的View可能造成事件无法被正常消费,因此所有事件都应该被给同一个View消费。
(二)实例演示
事件分发:Activity -> RootView -> ViewGroupA -> View1
事件回传:Activity <- RootView <- ViewGroupA <- View1
MainActivity
1 | /** |
RootView
1 | /** |
ViewGroupA
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/**
* @author [*昨日重现*] lhy_ycu@163.com
* @since version 1.0
*/
public class ViewGroupA extends RelativeLayout {
public ViewGroupA(Context context) {
super(context);
}
public ViewGroupA(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean dispatchTouchEvent(MotionEvent ev) {
DebugLog.e("ViewGroupA事件分发");
boolean result = super.dispatchTouchEvent(ev);
DebugLog.e("ViewGroupA事件回传," + result);
return result;
}
public boolean onInterceptTouchEvent(MotionEvent ev) {
boolean result = super.onInterceptTouchEvent(ev);
DebugLog.e("ViewGroupA事件拦截---" + result);
return result;
}
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
DebugLog.e("ViewGroupA事件消费---" + result);
return result;
}
}
View1
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
39/**
* @author [*昨日重现*] lhy_ycu@163.com
* @since version 1.0
*/
public class View1 extends View2 {
public View1(Context context) {
super(context);
}
public View1(Context context, AttributeSet attrs) {
super(context, attrs);
}
public boolean dispatchTouchEvent(MotionEvent ev) {
DebugLog.e("View1事件分发");
boolean result = super.dispatchTouchEvent(ev);
DebugLog.e("View1事件回传," + result);
return result;
}
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);//
DebugLog.e("View1事件消费---" + result);
// switch (event.getAction()) {
// case MotionEvent.ACTION_DOWN:
// DebugLog.e("View1按下...");
// break;
// case MotionEvent.ACTION_MOVE:
// DebugLog.e("View1移动...");
// break;
// case MotionEvent.ACTION_UP:
// DebugLog.e("View1释放...");
// break;
// }
return result;
}
}
打印结果如下
1 | [dispatchTouchEvent:109]MainActivity事件分发开始 |