一张图让你了解安卓事件处理流程的走向

来源:互联网 发布:centos怎么退出vi 编辑:程序博客网 时间:2024/05/22 17:37

转载请标明出处: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,再到ViewGroupdispatchTouchEvent,再到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
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    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"
    xmlns:tools="http://schemas.android.com/tools"
    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)diapatchTouchEventACTION_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,表示该状态(三种状态的一种)下的事件已经被消化了,该状态下的事件不再传递,但是并不影响其他状态(三种状态的一种)的事件处理。否则会继续向下传递事件


欢迎指出不足,没事下留言,你的支持,是我写下去的动力


4 0
原创粉丝点击