Android中Touch事件的傳遞機制

因爲以前對於android的事件傳遞機制不瞭解,今天正好不忙,趕忙抽出時間來理一下這方面的知識,本文結合demo,對android的事件傳遞機制進行分析。android

在事件傳遞過程當中,離不開如下三個方法:canvas

1.dispatchTouchEvent 分發touchEvent,返回值爲true時表示TouchEvent被當前View處理,事件不會向下層傳遞(包括後續的onInterceptTouchEvent和onTouchEvent),ide

dispatchTouchEvent會收到後續的ACTION_MOVE和ACTION_UP事件佈局

2.onInterceptTouchEvent 攔截touchEvent,返回true時表示當前View攔截了touchEvent,而後把事件交給當前View的onTouchEvent處理spa

3.onTouchEvent 處理TouchEvent,返回true時表示當前View消費了此事件,只有消費了前一個事件後才能收到後續事件。日誌

 

爲了弄清楚android在各層view的事件傳遞,我寫了一個小demo來分析Activity,ViewGroup,View之間的事件傳遞。code

首先來看一下代碼,一個自定義View,畫了一個矩形,在dispatchTouchEvent,onTouchEvent中加入日誌方便解析。xml

 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方法中加入logblog

 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的事件傳遞機制更瞭解了吧。

相關文章
相關標籤/搜索