Android 事件响应原理

一、简介

  触摸事件就是捕获触摸屏幕后产生的事件。Android为触摸事件封装了一个类——MotionEvent,如果重写onTouchEvent(MotionEvent event)方法,就会发现该方法的参数就是一个MotionEvent类实例。

  事件触发分为三个阶段,分发、拦截、消费。

二、事件拦截机制分析

  Android的View结构就是一个树形结构,View可以放在一个ViewGroup里面,这个ViewGroup又放在另一个ViewGroup里面,甚至还有可能继续嵌套,一层层地叠起来。触摸事件就一个,到底该分给谁?同一个事件,子View和父ViewGroup都有可能想要进行处理,因此就产生了“事件拦截机制”。

  假设,你所在公司,有一个总经理,级别最高;他下面有一个部长,级别次之;最低层,就是干活的你,没有级别。现在董事会交给总经理一个任务,总经理将这项任务布置给了部长,部长又把任务安排给了你。而当你完成了这项任务,你就把任务交给部长,部长觉得任务完成的不错,于是就签了他的名字,将任务交给了总经理,总经理看了也觉得不错,就也签了名字交给董事会。这个,一个任务就顺利完成了。

  一个总经理——MyViewGroupA,最外层的ViewGroup;

  一个部长——MyViewGroupB,中间层ViewGroup;

  一个干活的你——MyView,在最低层;

  对于ViewGroup,需要重写三个方法:

 1 @Override
 2 public boolean dispatchTouchEvent(MotionEvent event)
 3 {
 4     Log.d(TAG, "ViewGroup dispatchTouchEvent" + event.getAction());
 5     return super.dispatchTouchEvent(event);
 6 }
 7 
 8 @Override
 9 public boolean onInterceptTouchEvent(MotionEvent event)
10 {
11     Log.d(TAG, "ViewGroup onInterceptTouchEvent" + event.getAction());
12     return super.onInterceptTouchEvent(event);
13 }
14 
15 @Override
16 public boolean onTouchEvent(MotionEvent event)
17 {
18     Log.d(TAG, "ViewGroup onTouchEvent" + event.getAction());
19     return super.onTouchEvent(event);
20 }

 

  对于View,需要重写如下两个方法:

 1 @Override
 2 public boolean dispatchTouchEvent(MotionEvent event)
 3 {
 4     Log.d(TAG, "View dispatchTouchEvent" + event.getAction());
 5     return super.dispatchTouchEvent(event);
 6 }
 7 
 8 @Override
 9 public boolean onTouchEvent(MotionEvent event)
10 {
11     Log.d(TAG, "View onTouchEvent" + event.getAction());
12     return super.onTouchEvent(event);
13 }

  当点击View后的Log如下所示:

1 D/MyViewGroupA : ViewGroupA dispatchTouchEvent
2 D/MyViewGroupA : ViewGroupA onInterceptTouchEvent
3 D/MyViewGroupB : ViewGroupB dispatchTouchEvent
4 D/MyViewGroupB : ViewGroupB onInterceptTouchEvent
5 D/MyView : View dispatchTouchEvent
6 D/MyView : View onTouchEvent
7 D/MyViewGroupB : MyViewGroupB onTouchEvent
8 D/MyViewGroupA : MyViewGroupA onTouchEvent
  从其中可以看到,事件传递顺序是:

    总经理(MyViewGroupA)— 部长(MyViewGroupB)— 你(MyView),这个过程就是事件的分发阶段;

  PS:事件传递,即分发,就是执行dispatchTouchEvent()方法,再执行onInterceptTouchEvent()方法。

  事件的处理顺序:

    在你去干活的过程就是事件的目标阶段,即View的onTouchEvent()方法执行;

    你(MyView)— 部长(MyViewGroupB)— 总经理(MyViewGroupA),这个过程就是事件的向上传递消费阶段;

  PS:事件处理就是执行onTouchEvent()方法。

   假设1:总经理(MyViewGroupA)发现这个任务太简单了,觉得自己就可以完成,完全没必要再找下属。因此,事件就被总经理(MyViewGroupA)onInterceptTouchEvent()方法把事件拦截了,即让onInterceptTouchEvent()方法返回值为True,下面再看看Log:

1 D/MyViewGroupA : ViewGroupA dispatchTouchEvent
2 D/MyViewGroupA : ViewGroupA onInterceptTouchEvent
3 D/MyViewGroupA : MyViewGroupA onTouchEvent

  假设2:部长(MyViewGroupB)发现这个任务太简单了,觉得自己就可以完成,完全没必要再找下属。因此,事件就被部长(MyViewGroupB)onInterceptTouchEvent()方法把事件拦截了,即让onInterceptTouchEvent()方法返回值为True,下面再看看Log: 

1 D/MyViewGroupA : ViewGroupA dispatchTouchEvent
2 D/MyViewGroupA : ViewGroupA onInterceptTouchEvent
3 D/MyViewGroupB : ViewGroupB dispatchTouchEvent
4 D/MyViewGroupB : ViewGroupB onInterceptTouchEvent
5 D/MyViewGroupB : MyViewGroupB onTouchEvent
6 D/MyViewGroupA : MyViewGroupA onTouchEvent

  这两种情况下,可以看到总经理或者部长,MyViewGroupA或者MyViewGroupB将事件拦截了。

  事件的返回值含义:

  当事件在传递的过程中,返回值:True,拦截,不继续;False,不拦截,继续流程,传递给下一级。

  当事件在处理的过程中,返回值:True,处理了,不用审核了;False,交给上一级处理。

三、总结

1. dispatchTouchEvent(...):

  作用:决定事件是否由onInterceptTouchEvent拦截处理;

  当返回super.dispatchTouchEvent(...)时,由onInterceptTouchEvent来决定事件的流向,onInterceptTouchEvent返回值为false时,继续向子View分发事件,本View只处理ACTION_DOWN事件。

  当onInterceptTouchEvent返回值为true时,不继续向子View分发事件,本View处理所有事件。

2. onInterceptTouchEvent(...):

  作用:拦截事件,决定是否将事件传递给子View;

  当返回值为false时,事件继续传递给子View;

  当返回值为true时,事件交给onTouchEvent(...)处理,不再向子View传递。

3. onTouchEvent(MotionEvent event):

  作用:事件最终到这个方法,由此方法处理事件;

  当返回值为false时,事件继续向上传递给其父View的onTouchEvent()方法,直到根View的,一直传递到根View时,返回值都是false,也就是说在事件传递过程中没有View的onTouchEvent()方法返回true,此次手势就会结束,此次事件就会被取消。

  当返回值为true时,此View处理所有事件,并且处理的事件不会再向上返回。也就是说在值为true时,此手势的所有事件都传递给此View的onTouchEvent()处理,包括ACTION_DOWN、ACTION_UP、ACTION_MOVE等事件。

四、举例说明

  自定义三个View,分别为ViewEventA、ViewEventB、ViewEventC:

  1 // ViewEventA
  2 public class ViewEventA extends ViewGroup {
  3     private static final String TAG = "ViewEventA";
  4 
  5     public ViewEventA(Context context) {
  6         super(context);
  7     }
  8 
  9     @Override
 10     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 11         int count = getChildCount();
 12         for (int idx = 0; idx < count; idx++) {
 13             View child = getChildAt(idx);
 14             int widthMeSpec = MeasureSpec.makeMeasureSpec(300, MeasureSpec.EXACTLY);
 15             int heightmeSpec = MeasureSpec.makeMeasureSpec(300, MeasureSpec.EXACTLY);
 16             child.measure(widthMeSpec, heightmeSpec);
 17         }
 18         int widthMeSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
 19         int heightmeSpec = MeasureSpec.makeMeasureSpec(500, MeasureSpec.EXACTLY);
 20         setMeasuredDimension(widthMeSpec, heightmeSpec);
 21     }
 22 
 23     @Override
 24     protected void onLayout(boolean changed, int l, int t, int r, int b) {
 25         if (changed) {
 26             int count = getChildCount();
 27             for (int idx = 0; idx < count; idx++) {
 28                 View child = getChildAt(idx);
 29                 child.layout(0, 0, 300, 300);
 30             }
 31         }
 32     }
 33 
 34     @Override
 35     public boolean dispatchTouchEvent(MotionEvent event) {
 36         Log.d(TAG, "dispatchTouchEvent");
 37         return super.dispatchTouchEvent(event);
 38     }
 39 
 40     @Override
 41     public boolean onInterceptTouchEvent(MotionEvent event) {
 42         Log.d(TAG, "onInterceptTouchEvent");
 43         return super.onInterceptTouchEvent(event);
 44     }
 45 
 46     @Override
 47     public boolean onTouchEvent(MotionEvent event) {
 48         String action = "";
 49         switch (event.getAction()) {
 50             case MotionEvent.ACTION_DOWN:
 51                 action = "ACTION_DOWN";
 52                 break;
 53             case MotionEvent.ACTION_MOVE:
 54                 action = "ACTION_MOVE";
 55                 break;
 56             case MotionEvent.ACTION_UP:
 57                 action = "ACTION_UP";
 58                 break;
 59             case MotionEvent.ACTION_CANCEL:
 60                 action = "ACTION_CANCEL";
 61                 break;
 62         }
 63         Log.d(TAG, "onTouchEvent: " + action);
 64         return super.onTouchEvent(event);
 65     }
 66 }
 67 
 68 // ViewEventB
 69 public class ViewEventB extends ViewGroup {
 70     private static final String TAG = "ViewEventB";
 71 
 72     public ViewEventB(Context context) {
 73         super(context);
 74     }
 75 
 76     @Override
 77     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
 78         int count = getChildCount();
 79         for (int idx = 0; idx < count; idx++) {
 80             View child = getChildAt(idx);
 81             int width = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
 82             int height = MeasureSpec.makeMeasureSpec(100, MeasureSpec.EXACTLY);
 83             child.measure(width, height);
 84         }
 85 
 86         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
 87     }
 88 
 89     @Override
 90     protected void onLayout(boolean changed, int l, int t, int r, int b) {
 91         if (changed) {
 92             int count = getChildCount();
 93             for (int idx = 0; idx < count; idx++) {
 94                 View child = getChildAt(idx);
 95                 child.layout(0, 0, 100, 100);
 96             }
 97         }
 98     }
 99 
100     @Override
101     public boolean dispatchTouchEvent(MotionEvent event) {
102         Log.d(TAG, "dispatchTouchEvent");
103         return super.dispatchTouchEvent(event);
104     }
105 
106     @Override
107     public boolean onInterceptTouchEvent(MotionEvent event) {
108         Log.d(TAG, "onInterceptTouchEvent");
109         return super.onInterceptTouchEvent(event);
110     }
111 
112     @Override
113     public boolean onTouchEvent(MotionEvent event) {
114         String action = "";
115         switch (event.getAction()) {
116             case MotionEvent.ACTION_DOWN:
117                 action = "ACTION_DOWN";
118                 break;
119             case MotionEvent.ACTION_MOVE:
120                 action = "ACTION_MOVE";
121                 break;
122             case MotionEvent.ACTION_UP:
123                 action = "ACTION_UP";
124                 break;
125             case MotionEvent.ACTION_CANCEL:
126                 action = "ACTION_CANCEL";
127                 break;
128         }
129         Log.d(TAG, "onTouchEvent: " + action);
130         return super.onTouchEvent(event);
131     }
132 }
133 
134 // ViewEventC
135 public class ViewEventC extends ViewGroup {
136     private static final String TAG = "ViewEventC";
137 
138     public ViewEventC(Context context) {
139         super(context);
140     }
141 
142     @Override
143     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
144         setMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
145     }
146 
147     @Override
148     protected void onLayout(boolean changed, int l, int t, int r, int b) {
149 
150     }
151 
152     @Override
153     public boolean dispatchTouchEvent(MotionEvent event) {
154         Log.d(TAG, "dispatchTouchEvent");
155         return super.dispatchTouchEvent(event);
156     }
157 
158     @Override
159     public boolean onInterceptTouchEvent(MotionEvent event) {
160         Log.d(TAG, "onInterceptTouchEvent");
161         return super.onInterceptTouchEvent(event);
162     }
163 
164     @Override
165     public boolean onTouchEvent(MotionEvent event) {
166         String action = "";
167         switch (event.getAction()) {
168             case MotionEvent.ACTION_DOWN:
169                 action = "ACTION_DOWN";
170                 break;
171             case MotionEvent.ACTION_MOVE:
172                 action = "ACTION_MOVE";
173                 break;
174             case MotionEvent.ACTION_UP:
175                 action = "ACTION_UP";
176                 break;
177             case MotionEvent.ACTION_CANCEL:
178                 action = "ACTION_CANCEL";
179                 break;
180         }
181         Log.d(TAG, "onTouchEvent: " + action);
182         return super.onTouchEvent(event);
183     }
184 }    

  场景1:

  当本三个嵌套View中onTouchEvent()方法返回值均为false时,输出:

1 D/ViewEventA: dispatchTouchEvent
2 D/ViewEventA: onInterceptTouchEvent
3 D/ViewEventB: dispatchTouchEvent
4 D/ViewEventB: onInterceptTouchEvent
5 D/ViewEventC: dispatchTouchEvent
6 D/ViewEventC: onInterceptTouchEvent
7 D/ViewEventC: onTouchEvent: ACTION_DOWN
8 D/ViewEventB: onTouchEvent: ACTION_DOWN
9 D/ViewEventA: onTouchEvent: ACTION_DOWN

  从结果看出onTouchEvent()方法返回值均为false直到根View,事件消失了,手势被取消了。

  场景2:

  当本三个嵌套View中第二个View的onTouchEvent()方法返回值为true时,中间View,输出:

 1 D/ViewEventA: dispatchTouchEvent
 2 D/ViewEventA: onInterceptTouchEvent
 3 D/ViewEventB: dispatchTouchEvent
 4 D/ViewEventB: onInterceptTouchEvent
 5 D/ViewEventC: dispatchTouchEvent
 6 D/ViewEventC: onInterceptTouchEvent
 7 D/ViewEventC: onTouchEvent: ACTION_DOWN
 8 D/ViewEventB: onTouchEvent: ACTION_DOWN
 9 D/ViewEventA: dispatchTouchEvent
10 D/ViewEventA: onInterceptTouchEvent
11 D/ViewEventB: dispatchTouchEvent
12 D/ViewEventB: onTouchEvent: ACTION_MOVE
13 D/ViewEventA: dispatchTouchEvent
14 D/ViewEventA: onInterceptTouchEvent
15 D/ViewEventB: dispatchTouchEvent
16 D/ViewEventB: onTouchEvent: ACTION_MOVE
17 D/ViewEventA: dispatchTouchEvent
18 D/ViewEventA: onInterceptTouchEvent
19 D/ViewEventB: dispatchTouchEvent
20 D/ViewEventB: onTouchEvent: ACTION_UP

 

  从结果看出事件在向上冒泡返回时,发现第二个View的onTouchEvent()方法返回值为true时,此View消化了此次事件,并且此手势的其它事件均由第二个View处理。在第二个事件ACTION_UP从根View向下传递时,直到第二个View消化了此次事件,并且不再向下传递事件,同时,在事件被消化后也未再传递此事件。

  场景三:

  在第二个View中使用onInterceptTouchEvent()方法拦截事件,即第二个View的onInterceptTouchEvent()方法返回值为true,结果输出:

1 D/ViewEventA: dispatchTouchEvent
2 D/ViewEventA: onInterceptTouchEvent
3 D/ViewEventB: dispatchTouchEvent
4 D/ViewEventB: onInterceptTouchEvent
5 D/ViewEventB: onTouchEvent: ACTION_DOWN
6 D/ViewEventA: onTouchEvent: ACTION_DOWN

  从结果看出第二个View拦截了手势的ACTION_DOWN事件,事件未再向下(第三个)子View传递,再是在第二个View时,将事件交给了onTouchEvent()方法处理,但是由于第二个View的onTouchEvent()方法返回值为false,事件继续向上(父View)传递。发现没有View处理此次事件,手势被系统取消。

  场景4:

  当第二个View的onInterceptTouchEvent()方法和onTouchEvent()方法返回值均为true时,拦截事件并消化事件,结果输出:

 1 D/ViewEventA: onInterceptTouchEvent
 2 D/ViewEventB: dispatchTouchEvent
 3 D/ViewEventB: onInterceptTouchEvent
 4 D/ViewEventB: onTouchEvent: ACTION_DOWN
 5 D/ViewEventA: dispatchTouchEvent
 6 D/ViewEventA: onInterceptTouchEvent
 7 D/ViewEventB: dispatchTouchEvent
 8 D/ViewEventB: onTouchEvent: ACTION_MOVE
 9 D/ViewEventA: dispatchTouchEvent
10 D/ViewEventA: onInterceptTouchEvent
11 D/ViewEventB: dispatchTouchEvent
12 D/ViewEventB: onTouchEvent: ACTION_UP

  结果与场景二一致。

  

  

 

posted @ 2017-05-07 15:02  naray  阅读(1276)  评论(0编辑  收藏  举报