转载请标明出处:http://blog.csdn.net/qq1940879801/article/details/50136965
最近小明遇到问题老是问我,其中有一个问题是这样的:
小明:在ScrollView嵌套ListView,ListView无发滑动
一看就知道他不懂安卓事件处理机制,然后我耐心帮他讲解,最后虽然他懂了,但是我还是觉得有必要总结下安卓事件处理机制。
一.什么是事件
事件就是对触摸屏幕的操作,比如触摸事件,单机事件,滑动事件等。
用户对触摸屏幕的所有操作,都是先按下去,最后弹起来,按下去的时候可以进行一段移动,再弹起来,也就是说说有基本事件都包括这三种事件状态:
按下(ACTION_DOWM)
移动(ACTION_MOVE)
弹起(ACTION_UP)
安卓为响应这些事件,提供一个类MotionEvent,其中有这样一句话:
Motion events are always delivered to views as a consistent stream of events. What constitutes a consistent stream varies depending on the type of device. For touch events, consistency implies that pointers go down one at a time, move around as a group and then go up one at a time or are canceled.
翻译:请求事件总是依照一个始终如一的事件流传递给视图。什么是一个始终如一的流变化取决于设备的类型。对触摸事件,一致性意味着指针走一次,作为一个整体移动,然后向上一次或被取消。
也就是说事件从点击的子视图(View)开始,向上传递事件给父视图(ViewGroup),一直传递到传递到Activity。
而安卓的事件处理流程则是由Activity的dispatchTouchEvent,再到ViewGroup的dispatchTouchEvent,再到Viewgroup的onInterceptTouchEvent,一直传递到点击的子视图View
二.view的事件分发以及viewgroup的事件分发和拦截
通过查看view的源码,发现view只有dispatchTouchEvent,OnTouchEvent
通过查看Viewgroup的源码,发现viewgroup不仅有dispatchTouchEvent,OnTouchEvent,还有onInterceptTouchEvent
dispatchTouchEvent:方法用于事件的分发,Android中的所有事件都必须经过此方法的分发,然后决定是自身消费当前事件还是继续向下分发给 视图处理。返回true表示不在继续分发,事件被消费了。返回false则事件继续分发,如果是viewgroup则分发给onInterceptTouchEvent进行判断是否拦截
OnTouchEvent:方法用于事件的处理,返回true表示消费处理当前事件,返回false则不处理,交给子控件进行继续分发。
onInterceptTouchEvent:是ViewGroup中才有的方法,View中没有,它的作用是负责事件的拦截,返回true的时候表示拦截当前事件,不继续往下分发,交给自身的onTouchEvent进行处理。返回false则不拦截,继续往下传。这是ViewGroup特有的方法,因为ViewGroup中可能还有子View,而在Android中View中是不能再包含子View的
三.从一个案例让你了解事件执行流程
案例1:view的事件分发
1.自定义类JesseButton继承Button
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
import
android.content.Context;
import
android.util.AttributeSet;
import
android.util.Log;
import
android.view.MotionEvent;
import
android.widget.Button;
/**
* @ClassName:JesseButton
* @Description: TODO
* @Author: Jesse
* @Date: 2015-12-1 下午9:16:31
* @Copyright: 2015 Jesse Inc. All rights reserved.
*/
public
class
JesseButton
extends
Button {
public
static
final
String TAG = JesseButton.
class
.getSimpleName();
public
JesseButton(Context context, AttributeSet attrs) {
super
(context, attrs);
}
@Override
public
boolean
dispatchTouchEvent(MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
Log.e(TAG,
"dispatchTouchEvent ACTION_DOWN"
);
break
;
case
MotionEvent.ACTION_MOVE:
Log.e(TAG,
"dispatchTouchEvent ACTION_MOVE"
);
break
;
case
MotionEvent.ACTION_UP:
Log.e(TAG,
"dispatchTouchEvent ACTION_UP"
);
break
;
}
return
super
.dispatchTouchEvent(event);
}
@Override
public
boolean
onTouchEvent(MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
Log.e(TAG,
"onTouchEvent ACTION_DOWN"
);
break
;
case
MotionEvent.ACTION_MOVE:
Log.e(TAG,
"onTouchEvent ACTION_MOVE"
);
break
;
case
MotionEvent.ACTION_UP:
Log.e(TAG,
"onTouchEvent ACTION_UP"
);
break
;
}
return
super
.onTouchEvent(event);
}
}
|
2.重写dispatchTouchEvent和onTouchEvent方法
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
|
import
android.app.Activity;
import
android.os.Bundle;
import
android.util.Log;
import
android.view.MotionEvent;
import
android.view.View;
import
android.view.View.OnClickListener;
import
android.view.View.OnTouchListener;
import
android.widget.Button;
/**
* @ClassName:MainActivity
* @Description: TODO
* @Author: Jesse
* @Date: 2015-12-1 下午9:10:35
* @Copyright: 2015 Jesse Inc. All rights reserved.
*/
public
class
MainActivity
extends
Activity {
private
static
final
String TAG = MainActivity.
class
.getSimpleName();
private
static
final
String JESSETAG = JesseButton.TAG;
@Override
protected
void
onCreate(Bundle savedInstanceState) {
super
.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn_click=(Button) findViewById(R.id.btn_click);
btn_click.setOnTouchListener(
new
OnTouchListener() {
@Override
public
boolean
onTouch(View v, MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
Log.e(JESSETAG,
"onTouch ACTION_DOWN"
);
break
;
case
MotionEvent.ACTION_MOVE:
Log.e(JESSETAG,
"onTouch ACTION_MOVE"
);
break
;
case
MotionEvent.ACTION_UP:
Log.e(JESSETAG,
"onTouch ACTION_UP"
);
break
;
}
return
false
;
}
});
btn_click.setOnClickListener(
new
OnClickListener() {
@Override
public
void
onClick(View v) {
Log.e(JESSETAG,
"btn onclick"
);
}
});
}
@Override
public
boolean
dispatchTouchEvent(MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
Log.e(TAG,
"dispatchTouchEvent ACTION_DOWN"
);
break
;
case
MotionEvent.ACTION_MOVE:
Log.e(TAG,
"dispatchTouchEvent ACTION_MOVE"
);
break
;
case
MotionEvent.ACTION_UP:
Log.e(TAG,
"dispatchTouchEvent ACTION_UP"
);
break
;
}
return
super
.dispatchTouchEvent(event);
}
@Override
public
boolean
onTouchEvent(MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
Log.e(TAG,
"onTouchEvent ACTION_DOWN"
);
break
;
case
MotionEvent.ACTION_MOVE:
Log.e(TAG,
"onTouchEvent ACTION_MOVE"
);
break
;
case
MotionEvent.ACTION_UP:
Log.e(TAG,
"onTouchEvent ACTION_UP"
);
break
;
}
return
super
.onTouchEvent(event);
}
}
|
res/layout/activity_main.xml文件如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
android:layout_width
=
"match_parent"
android:layout_height
=
"match_parent"
android:orientation
=
"vertical"
tools:context
=
"com.jesse.MainActivity"
>
<
com.jesse.JesseButton
android:id
=
"@+id/btn_click"
android:layout_width
=
"wrap_content"
android:layout_height
=
"wrap_content"
android:text
=
"btn_click"
/>
</
LinearLayout
>
|
输出结果:
温馨提示:这里鼠标要稍微滑动下,才能响应事件的移动状态(即才会有ACTION_MOVE)
由此可以看出,View事件执行流程是:
(Activity)dispatchTouchEvent-->(Button)dispatchTouchEvent-->(Button)onTouch-->(Button)onTouchEvent
案例2:viewgroup的事件分发和事件拦截
在原来基础上新增一个类JesseLinearLayout
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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
import
android.content.Context;
import
android.util.AttributeSet;
import
android.util.Log;
import
android.view.MotionEvent;
import
android.widget.LinearLayout;
/**
* @ClassName:JesseLinearLayout
* @Description: TODO
* @Author: Jesse
* @Date: 2015-12-2 上午9:34:26
* @Copyright: 2015 Jesse Inc. All rights reserved.
*/
public
class
JesseLinearLayout
extends
LinearLayout {
private
static
final
String TAG = JesseLinearLayout.
class
.getSimpleName();
public
JesseLinearLayout(Context context, AttributeSet attrs) {
super
(context, attrs);
}
@Override
public
boolean
dispatchTouchEvent(MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
Log.e(TAG,
"dispatchTouchEvent ACTION_DOWN"
);
break
;
case
MotionEvent.ACTION_MOVE:
Log.e(TAG,
"dispatchTouchEvent ACTION_MOVE"
);
break
;
case
MotionEvent.ACTION_UP:
Log.e(TAG,
"dispatchTouchEvent ACTION_UP"
);
break
;
}
return
super
.dispatchTouchEvent(event);
}
@Override
public
boolean
onInterceptTouchEvent(MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
Log.e(TAG,
"onInterceptTouchEvent ACTION_DOWN"
);
break
;
case
MotionEvent.ACTION_MOVE:
Log.e(TAG,
"onInterceptTouchEvent ACTION_MOVE"
);
break
;
case
MotionEvent.ACTION_UP:
Log.e(TAG,
"onInterceptTouchEvent ACTION_UP"
);
break
;
}
return
super
.onInterceptTouchEvent(event);
}
@Override
public
boolean
onTouchEvent(MotionEvent event) {
switch
(event.getAction()) {
case
MotionEvent.ACTION_DOWN:
Log.e(TAG,
"onTouchEvent ACTION_DOWN"
);
break
;
case
MotionEvent.ACTION_MOVE:
Log.e(TAG,
"onTouchEvent ACTION_MOVE"
);
break
;
case
MotionEvent.ACTION_UP:
Log.e(TAG,
"onTouchEvent ACTION_UP"
);
break
;
}
return
super
.onTouchEvent(event);
}
}
|
res/layout/activity_main.xml文件改为如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
<com.jesse.JesseLinearLayout xmlns:android=
"http://schemas.android.com/apk/res/android"
android:layout_width=
"match_parent"
android:layout_height=
"match_parent"
android:orientation=
"vertical"
tools:context=
"com.jesse.MainActivity"
>
<com.jesse.JesseButton
android:id=
"@+id/btn_click"
android:layout_width=
"wrap_content"
android:layout_height=
"wrap_content"
android:text=
"btn_click"
/>
</com.jesse.JesseLinearLayout>
|
运行结果:
由此可以看出,Viewgroup事件执行流程是:
(Activity)dispatchTouchEvent-->(LinearLayout)diapatchTouchEvent-->(LinearLayout)onInterceptTouchEvent-->(Button)dispatchTouchEvent-->(Button)onTouch-->(Button)onTouchEvent
四.一张图让你了解事件处理流程的走向
默认执行流程是
(Activity)dispatchTouchEvent(ACTION_DOWM返回false)-->(LinearLayout)diapatchTouchEvent(ACTION_DOWM返回false)-->(LinearLayout)onInterceptTouchEvent(ACTION_DOWM返回false)-->(Button)dispatchTouchEvent(返回false)-->(Button)onTouch(返回false)-->(Button)onTouchEvent
假如在(LinearLayout)diapatchTouchEvent的ACTION_DOWM返回true,三种状态执行的事件是:
ACTION_DOWM状态执行的事件是:(Activity)dispatchTouchEvent(ACTION_DOWM返回false)-->(LinearLayout)diapatchTouchEvent(ACTION_DOWM返回false)
ACTION_MOVE状态执行的事件是:(Activity)dispatchTouchEvent(ACTION_MOVE返回false)-->(LinearLayout)diapatchTouchEvent(ACTION_MOVE返回false)-->(LinearLayout)onInterceptTouchEvent
ACTION_UP状态执行的事件是:(Activity)dispatchTouchEvent(ACTION_UP返回false)-->(LinearLayout)diapatchTouchEvent(ACTION_UP返回false)-->(LinearLayout)onInterceptTouchEvent
如果我们不想ACTION_UP状态的(LinearLayout)onInterceptTouchEvent执行,只需要(LinearLayout)diapatchTouchEvent(ACTION_UP返回true)即:
ACTION_UP状态执行的事件是:(Activity)dispatchTouchEvent(ACTION_UP返回false)-->(LinearLayout)diapatchTouchEvent(ACTION_UP返回true)
五.解释和总结:
继续重复-----事件包括最基本的三种状态:按下(ACTION_DOWM),移动(ACTION_MOVE),弹起(ACTION_UP)
1.我们单击view,这时候事件由view通过MotionEvent向上传递事件,先传递给ViewGroup再通过MotionEvent向上传递事件,直到Activity
2.事件默认处理流程则相反,由Activity的dispatchTouchEvent向下传递事件,先传递给ViewGroup的dispatchTouchEvent,再传递给ViewGroup的onInterceptTouchEvent,最后传递到View的dispatchTouchEvent
3.由dispatchTouchEvent和onInterceptTouchEvent的ACTION_DOWM决定该事件流程的走向(由dispatchTouchEvent决定是否分发给onInterceptTouchEvent,再通过onInterceptTouchEvent决定是否拦截)。这里只是决定事件的一个流程走向,至于某种状态(三种状态的一种)按照这个流程走向是否执行完所有流程,请看下面一条。
4.如果事件处理方法返回true,表示该状态(三种状态的一种)下的事件已经被消化了,该状态下的事件不再传递,但是并不影响其他状态(三种状态的一种)的事件处理。否则会继续向下传递事件
欢迎指出不足,没事下留言,你的支持,是我写下去的动力