1. 程式人生 > >android觸發事件傳遞機制

android觸發事件傳遞機制

一 事件傳遞的三個階段
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);
    }
}
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