Android中Touch事件的传递机制
由于之前对于android的事件传递机制不了解,今天正好不忙,赶紧抽出时间来理一下这方面的知识,本文结合demo,对android的事件传递机制进行分析。
在事件传递过程中,离不开以下三个方法:
1.dispatchTouchEvent 分发touchEvent,返回值为true时表示TouchEvent被当前View处理,事件不会向下层传递(包括后续的onInterceptTouchEvent和onTouchEvent),
dispatchTouchEvent会收到后续的ACTION_MOVE和ACTION_UP事件
2.onInterceptTouchEvent 拦截touchEvent,返回true时表示当前View拦截了touchEvent,然后把事件交给当前View的onTouchEvent处理
3.onTouchEvent 处理TouchEvent,返回true时表示当前View消费了此事件,只有消费了前一个事件后才能收到后续事件。
为了弄清楚android在各层view的事件传递,我写了一个小demo来分析Activity,ViewGroup,View之间的事件传递。
首先来看一下代码,一个自定义View,画了一个矩形,在dispatchTouchEvent,onTouchEvent中加入日志方便解析。
1 public class DrawRectView extends View {
2
3 private Paint mPaint;
4
5 public DrawRectView(Context context) {
6 super(context);
7 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
8 }
9
10 public DrawRectView(Context context, AttributeSet set) {
11 super(context, set);
12 mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
13 }
14
15 @Override
16 public boolean dispatchTouchEvent(MotionEvent event) {
17 Log.v(LogUtils.TAG, "DrawRectView dispatchTouchEvent action=" + event.getAction());
18 return super.dispatchTouchEvent(event);
19 }
20
21
22 @Override
23 protected void onDraw(Canvas canvas) {
24 super.onDraw(canvas);
25 mPaint.setColor(Color.YELLOW);
26 canvas.drawRect(0, 0, 300, 300, mPaint);
27 }
28
29 @Override
30 public boolean onTouchEvent(MotionEvent event) {
31 Log.v(LogUtils.TAG, "DrawRectView onTouchEvent action=" + event.getAction());
32 return super.onTouchEvent(event);
33 }
34 }
自定义Layout,同样在相关的TouchEvent方法中加入log
1 public class MyLayout extends RelativeLayout {
2
3 public MyLayout(Context context, AttributeSet attrs) {
4 super(context, attrs);
5 }
6
7 @Override
8 public boolean dispatchTouchEvent(MotionEvent ev) {
9 Log.v(LogUtils.TAG, "MyLayout dispatchTouchEvent action=" + ev.getAction());
10 return super.dispatchTouchEvent(ev);
11 }
12
13 @Override
14 public boolean onInterceptTouchEvent(MotionEvent ev) {
15 Log.v(LogUtils.TAG, "MyLayout onInterceptTouchEvent action=" + ev.getAction());
16 return super.onInterceptTouchEvent(ev);
17 }
18
19 @Override
20 public boolean onTouchEvent(MotionEvent event) {
21 Log.v(LogUtils.TAG, "MyLayout onTouchEvent event=" + event.getAction());
22 return super.onTouchEvent(event);
23 }
接下来是Activity与activity的布局
1 public class TouchTestActivity extends Activity {
2
3 private DrawRectView mDrawRectView;
4
5 @Override
6 protected void onCreate(Bundle savedInstanceState) {
7 super.onCreate(savedInstanceState);
8 setContentView(R.layout.touch_test_activity);
9
10 mDrawRectView = (DrawRectView) findViewById(R.id.draw_rect_view);
11 mDrawRectView.setOnTouchListener(new OnTouchListener() {
12 @Override
13 public boolean onTouch(View v, MotionEvent event) {
14 Log.v(LogUtils.TAG, "mDrawRectView OnTouchListener action=" + event.getAction());
15 return false;
16 }
17 });
18 }
19
20 @Override
21 public boolean dispatchTouchEvent(MotionEvent ev) {
22 Log.v(LogUtils.TAG, "TouchTestActivity dispatchTouchEvent action=" + ev.getAction());
23 return super.dispatchTouchEvent(ev);
24 }
25
26 @Override
27 public boolean onTouchEvent(MotionEvent event) {
28 Log.v(LogUtils.TAG, "TouchTestActivity onTouchEvent action=" + event.getAction());
29 return super.onTouchEvent(event);
30 }
31 }
1 <com.yangy.test.custom_view.MyLayout xmlns:android="http://schemas.android.com/apk/res/android"
2 android:layout_width="match_parent"
3 android:layout_height="match_parent" >
4
5 <com.yangy.test.custom_view.DrawRectView
6 android:id="@+id/draw_rect_view"
7 android:layout_width="300dp"
8 android:layout_height="300dp"
9 android:layout_centerInParent="true" />
10
11 </com.yangy.test.custom_view.MyLayout>
当我们按下矩形DrawRectView时,可以看到打印的log信息如下,Android Touch事件自上到下传递,Activity-->ViewGroup-->View
11-24 15:19:40.659: V/--DEBUG--(32570): TouchTestActivity dispatchTouchEvent action=ACTION_DOWN
11-24 15:19:40.659: V/--DEBUG--(32570): MyLayout dispatchTouchEvent action=ACTION_DOWN
11-24 15:19:40.659: V/--DEBUG--(32570): MyLayout onInterceptTouchEvent action=ACTION_DOWN
11-24 15:19:40.659: V/--DEBUG--(32570): DrawRectView dispatchTouchEvent action=ACTION_DOWN
11-24 15:19:40.659: V/--DEBUG--(32570): mDrawRectView OnTouchListener action=ACTION_DOWN
11-24 15:19:40.669: V/--DEBUG--(32570): DrawRectView onTouchEvent action=ACTION_DOWN
11-24 15:19:40.669: V/--DEBUG--(32570): MyLayout onTouchEvent event=ACTION_DOWN
11-24 15:19:40.669: V/--DEBUG--(32570): TouchTestActivity onTouchEvent action=ACTION_DOWN
11-24 15:19:40.689: V/--DEBUG--(32570): TouchTestActivity dispatchTouchEvent action=ACTION_UP
11-24 15:19:40.689: V/--DEBUG--(32570): TouchTestActivity onTouchEvent action=ACTION_UP
根据log信息,我们也就知道了整个View的事件传递流程,可用下图表示,这里值得注意的是没有任何View消耗掉ACTION_DOWN事件,
所以后续的ACTION_MOVE和ACTION_UP事件并不会向下传递了,这个从log中也可看出。
这时把DrawRectView的onTouchEvent方法返回true,则会出现什么结果呢,接着看log
11-24 16:04:03.159: V/--DEBUG--(3037): TouchTestActivity dispatchTouchEvent action=ACTION_DOWN
11-24 16:04:03.159: V/--DEBUG--(3037): MyLayout dispatchTouchEvent action=ACTION_DOWN
11-24 16:04:03.159: V/--DEBUG--(3037): MyLayout onInterceptTouchEvent action=ACTION_DOWN
11-24 16:04:03.159: V/--DEBUG--(3037): DrawRectView dispatchTouchEvent action=ACTION_DOWN
11-24 16:04:03.159: V/--DEBUG--(3037): mDrawRectView OnTouchListener action=ACTION_DOWN
11-24 16:04:03.159: V/--DEBUG--(3037): DrawRectView onTouchEvent action=ACTION_DOWN
11-24 16:04:03.219: V/--DEBUG--(3037): TouchTestActivity dispatchTouchEvent action=ACTION_MOVE
11-24 16:04:03.219: V/--DEBUG--(3037): MyLayout dispatchTouchEvent action=ACTION_MOVE
11-24 16:04:03.219: V/--DEBUG--(3037): MyLayout onInterceptTouchEvent action=ACTION_MOVE
11-24 16:04:03.219: V/--DEBUG--(3037): DrawRectView dispatchTouchEvent action=ACTION_MOVE
11-24 16:04:03.219: V/--DEBUG--(3037): mDrawRectView OnTouchListener action=ACTION_MOVE
11-24 16:04:03.219: V/--DEBUG--(3037): DrawRectView onTouchEvent action=ACTION_MOVE
11-24 16:04:03.249: V/--DEBUG--(3037): TouchTestActivity dispatchTouchEvent action=ACTION_UP
11-24 16:04:03.249: V/--DEBUG--(3037): MyLayout dispatchTouchEvent action=ACTION_UP
11-24 16:04:03.249: V/--DEBUG--(3037): MyLayout onInterceptTouchEvent action=ACTION_UP
11-24 16:04:03.249: V/--DEBUG--(3037): DrawRectView dispatchTouchEvent action=ACTION_UP
11-24 16:04:03.249: V/--DEBUG--(3037): mDrawRectView OnTouchListener action=ACTION_UP
11-24 16:04:03.249: V/--DEBUG--(3037): DrawRectView onTouchEvent action=ACTION_UP
看来DrawRectView的onTouchEvent方法消费掉ACTION_DOWN事件后,ACTION_MOVE与ACTION_UP都传递过来了,而因为消费了事件,所以onTouchEvent 并不会向上传递
如果在ViewGroup中拦截了TouchEvent事件又会怎么样呢,由下图来说明:
经过本文的说明后,相信你对于android的事件传递机制更了解了吧。