一 事件傳遞的三個階段
1 分發(Dispatch):
事件的分發對應着dispatchTouchEvent方法,在Android系統中,所有的觸摸事件的分發都是由改方法分發。
public booleandispatchTouchEvent(MotionEvent event) {..}
說明:在這個方法中,當前視圖將根據返回值決定是否把這個事件繼續傳給子視圖,如果返回true當前視圖會直接消費掉該事件,不再分發給其子視圖;如果返回super.dispatchTouchEvent(event)
,當前視圖會把該事件分發給其子視圖。如果當前視圖是ViewGroup或其子類的話,還要調用onInterceptTouchEvent方法判斷該事件是否被攔截。
2 攔截(Intercept):
事件的攔截,對應如下的方法:
public booleanonInterceptTouchEvent(MotionEvent ev) {...}
說明:這個方法只有在ViewGroup及其子類中有,它們通過返回值來判斷是否攔截當前事件,如果返回true會攔截當前事件,不再分發給它們的子視圖,同時會調用onTouchEvent消費該事件;如果返回false或super.onInterceptTouchEvent,那麼它們會把當前事件分發給它們的子視圖。
3 消費:
事件的消費,對應如下的方法:
public booleanonTouchEvent(MotionEvent ev) {...}
說明:該方法返回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);
}
}
從上面得出如下結論:
(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 打印日誌: