因爲以前對於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的事件傳遞機制更瞭解了吧。