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

转载请标明出处: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,表示该状态(三种状态的一种)下的事件已经被消化了,该状态下的事件不再传递,但是并不影响其他状态(三种状态的一种)的事件处理。否则会继续向下传递事件


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


评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值