android觸發事件傳遞機制
一 事件傳遞的三個階段
1 分發(Dispatch):
事件的分發對應著dispatchTouchEvent方法,在Android系統中,所有的觸控事件的分發都是由改方法分發。
public booleandispatchTouchEvent(MotionEvent event) {..}
說明:在這個方法中,當前檢視將根據返回值決定是否把這個事件繼續傳給子檢視,如果返回true當前檢視會直接消費掉該事件,不再分發給其子檢視;如果返回super.dispatchTouchEvent(event)
,當前檢視會把該事件分發給其子檢視。如果當前檢視是ViewGroup或其子類的話,還要呼叫onInterceptTouchEvent方法判斷該事件是否被攔截。
事件的攔截,對應如下的方法:
public booleanonInterceptTouchEvent(MotionEvent ev) {...}
說明:這個方法只有在ViewGroup及其子類中有,它們通過返回值來判斷是否攔截當前事件,如果返回true會攔截當前事件,不再分發給它們的子檢視,同時會呼叫onTouchEvent消費該事件;如果返回false或super.onInterceptTouchEvent,那麼它們會把當前事件分發給它們的子檢視。
3 消費:
事件的消費,對應如下的方法:
public booleanonTouchEvent(MotionEventev) {...}
說明:該方法返回true則表示當前檢視處理相應的事件,事件不會向上傳遞給父檢視;返回false則表示當前檢視不處理相應的事件,會把這個事件向上傳遞給父檢視onTouchEvent方法進行處理。
Android系統中有事件傳遞能力的類有:
Activity:有dispatchTouchEvent、onTouchEvent兩個方法。
ViewGroup:有dispatchTouchEvent、onTouchEvent、onInterceptTouchEvent三個方法。
View:有dispatchTouchEvent、onTouchEvent兩個方法。如下:
二 View事件傳遞
1 自定義TextView
public class MyTextView extends TextView { public static final String TAG = MyTextView.class.getSimpleName() + "======> "; public MyTextView(Context context) { super(context); } public MyTextView(Context context, AttributeSet attrs) { super(context, attrs); } public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) public MyTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } @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佈局檔案:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.eric.project.MainActivity"> <com.eric.project.event.MyTextView android:id="@+id/txt_eventView" android:layout_centerInParent="true" android:textSize="36sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View 事件傳遞" /> </RelativeLayout>
3 Activity程式碼:
public class MainActivity extends AppCompatActivity { public static final String TAG = MainActivity.class.getSimpleName() + "======> "; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); TextView txt_eventView = (TextView) findViewById(R.id.txt_eventView); txt_eventView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Log.e(TAG," onClick "); } }); //如果不設定觸控監聽不會觸發onTouchEvent方法 txt_eventView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()){ case MotionEvent.ACTION_DOWN: Log.e(TAG," onTouch->ACTION_DOWN "); break; case MotionEvent.ACTION_MOVE: Log.e(TAG," onTouch->ACTION_MOVE "); break; case MotionEvent.ACTION_UP: Log.e(TAG," onTouch->ACTION_UP "); break; } return false; } }); } @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); } }4 列印結果如下: 01-31 05:09:36.158 14865-14865/com.eric.project E/MainActivity======>: dispatchTouchEvent->ACTION_DOWN 01-31 05:09:36.158 14865-14865/com.eric.project E/MyTextView======>: dispatchTouchEvent->ACTION_DOWN 01-31 05:09:36.159 14865-14865/com.eric.project E/MainActivity======>: onTouch->ACTION_DOWN 01-31 05:09:36.159 14865-14865/com.eric.project E/MyTextView======>: onTouchEvent->ACTION_DOWN 01-31 05:09:36.254 14865-14865/com.eric.project E/MainActivity======>: dispatchTouchEvent->ACTION_UP 01-31 05:09:36.254 14865-14865/com.eric.project E/MyTextView======>: dispatchTouchEvent->ACTION_UP 01-31 05:09:36.254 14865-14865/com.eric.project E/MainActivity======>: onTouch->ACTION_UP 01-31 05:09:36.254 14865-14865/com.eric.project E/MyTextView======>: onTouchEvent->ACTION_UP 01-31 05:09:36.257 14865-14865/com.eric.project E/MainActivity======>: onClick
從上面得出如下結論:
(1)觸控事件的傳遞流程是從dispatchTouchEvent開始,如果不進行干預(即返回super.dispatchTouchEvent(ev)),則事件將會依照潛逃層次從外層向內層傳遞,到達最底層的View時,就由它的onTouchEvent方法進行處理,該方法如果能夠消費該事件,就會返回true,如果處理不了,就返回false,這時會重新向外層傳遞,並由外層View的onTouchEvent方法進行處理,以此類推。
(2)如果事件在向內層傳遞的過程中人為干預了,時間函式返回true,則會導致事件提前被消費掉,內層的View不能收到事件。
(3)如果最內層的View能夠接受到事件,那麼它先執行的是onTouch方法,然後才執行onClick方法。如果onTouch返回true,則事件不會繼續傳遞,最後也不會呼叫onClick方法;如果返回super.onTouch或false,事件會繼續傳遞。流程圖如下:
三 ViewGroup事件傳遞
1 自定義ViewGroup
public class MyViewGroup extends LinearLayout { public static final String TAG = MyViewGroup.class.getSimpleName() + "======> "; public MyViewGroup(Context context) { super(context); } public MyViewGroup(Context context, AttributeSet attrs) { super(context, attrs); } public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @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); } @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); } }
2 佈局檔案:
<?xml version="1.0" encoding="utf-8"?> <com.eric.project.event.MyViewGroup 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:gravity="center" tools:context="com.eric.project.MainActivity"> <com.eric.project.event.MyTextView android:id="@+id/txt_eventView" android:layout_centerInParent="true" android:textSize="36sp" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="View 事件傳遞" /> </com.eric.project.event.MyViewGroup>
4 列印日誌:
02-01 15:49:55.721 23305-23305/com.eric.project E/MainActivity======>: dispatchTouchEvent->ACTION_DOWN 02-01 15:49:55.722 23305-23305/com.eric.project E/MyViewGroup======>: dispatchTouchEvent->ACTION_DOWN 02-01 15:49:55.722 23305-23305/com.eric.project E/MyViewGroup======>: onInterceptTouchEvent->ACTION_DOWN