1. 程式人生 > >Android的事件分發(dispatchTouchEvent),攔截(onInterceptTouchEvent)與處理(onTouchEvent)

Android的事件分發(dispatchTouchEvent),攔截(onInterceptTouchEvent)與處理(onTouchEvent)

在Android中,View的結構是樹狀的,所以,當觸發觸控事件的時候,其事件傳遞也是從上之下一層層的傳遞。下面我們結合例子來一點點進行分析。

首先,我們需要了解事件處理中的幾個方法:

1、在ViewGroup中,事件分為dispatchTouchEvent(事件的分發),onInterceptTouchEvent(事件的攔截),onTouchEvent(事件的處理)。

2、在View中,事件分為dispatchTouchEvent(事件的分發),onTouchEvent(事件的處理)。

下面是demo的介面結構,它是由兩個自定義的ViewGroup和一個自定義的View組成,並分別重寫了它們的以上幾個方法。

 圖片

其中MyViewGroupA程式碼如下:

public class MyViewGroupA extends LinearLayout {
    public MyViewGroupA(Context context) {
        super(context);
    }
 
    public MyViewGroupA(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("MyViewGroupA","dispatchTouchEvent_ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
 
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("MyViewGroupA","onInterceptTouchEvent_ACTION_UP");
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("MyViewGroupA","onTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("MyViewGroupA","onTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("MyViewGroupA","onTouchEvent_ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }
}

MyViewGroupB程式碼如下:

public class MyViewGroupB extends LinearLayout {
    public MyViewGroupB(Context context) {
        super(context);
    }
 
    public MyViewGroupB(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("MyViewGroupB","dispatchTouchEvent_ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(ev);
    }
 
    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("MyViewGroupB","onInterceptTouchEvent_ACTION_UP");
                break;
        }
        return super.onInterceptTouchEvent(ev);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("MyViewGroupB","onTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("MyViewGroupB","onTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("MyViewGroupB","onTouchEvent_ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }
}

MyView程式碼如下:

public class MyView extends View {
    public MyView(Context context) {
        super(context);
    }
 
    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }
 
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("MyView","dispatchTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("MyView","dispatchTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("MyView","dispatchTouchEvent_ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(event);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("MyView","onTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("MyView","onTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("MyView","onTouchEvent_ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }
}

我們說過,事件傳遞是由上到下的,所以最外層的View首先對事件進行操作。而我們最外層是Activity,所以事件也是從這裡開始。
Activity程式碼如下:

public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("Activity","dispatchTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("Activity","dispatchTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("Activity","dispatchTouchEvent_ACTION_UP");
                break;
        }
        return super.dispatchTouchEvent(event);
    }
 
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                Log.i("Activity","onTouchEvent_ACTION_DOWN");
                break;
            case MotionEvent.ACTION_MOVE:
                Log.i("Activity","onTouchEvent_ACTION_MOVE");
                break;
            case MotionEvent.ACTION_UP:
                Log.i("Activity","onTouchEvent_ACTION_UP");
                break;
        }
        return super.onTouchEvent(event);
    }
}

現在我們通過觸控MyView開始進行分析。雖然dispatchTouchEvent是事件開始的第一步,但是在開發中,我們通常很少改寫它,所以我們下面只討論其他兩個方法。
1、對以上方法均不作處理,都返回super。這意味著我們既不攔截,也不消費。

大家看輸出結果:

I/Activity: dispatchTouchEvent_ACTION_DOWN


I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN


I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN


I/MyView: dispatchTouchEvent_ACTION_DOWN
I/MyView: onTouchEvent_ACTION_DOWN


I/MyViewGroupB: onTouchEvent_ACTION_DOWN
I/MyViewGroupA: onTouchEvent_ACTION_DOWN
I/Activity: onTouchEvent_ACTION_DOWN


I/Activity: dispatchTouchEvent_ACTION_MOVE
I/Activity: onTouchEvent_ACTION_MOVE


I/Activity: dispatchTouchEvent_ACTION_UP
I/Activity: onTouchEvent_ACTION_UP

結合輸出結果,我們可以總結出以下的結論:
 

結合流程圖,不難發現,如果我對事件既不攔截,也不消費,當觸發ACTION_DOWN的時候,事件會經過Activity——MyViewGroupA——MyViewGroupB——MyView一層層的向下進行dispatchTouchEvent(分發)—onInterceptTouchEvent(攔截)呼叫。當到達最底層MyView後,開始觸發消費操作,因為我均不消費,ACTION_DOWN將由底層一層層向上冒,移交上層處理。當抵達最上層Activity後,說明下層均不消費,之後觸發的ACTION_MOVE和ACTION_UP將不再向下層分發傳遞,直接交由Activity分發給自己進行處理。

2、我們將MyVIewGroupB的onInterceptTouchEvent返回值改為true,其他均是super。這意味著僅僅MyViewGroupB進行事件攔截,但均無消費

輸出結果如下:

I/Activity: dispatchTouchEvent_ACTION_DOWN


I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN


I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN


I/MyViewGroupB: onTouchEvent_ACTION_DOWN
I/MyViewGroupA: onTouchEvent_ACTION_DOWN
I/Activity: onTouchEvent_ACTION_DOWN


I/Activity: dispatchTouchEvent_ACTION_MOVE
I/Activity: onTouchEvent_ACTION_MOVE


I/Activity: dispatchTouchEvent_ACTION_UP
I/Activity: onTouchEvent_ACTION_UP

結合輸出結果,總結如下:
 

當觸發ACTION_DOWN的時候,事件依然是從Activity開始一層層向下傳遞,當傳遞到MyViewGroupB時,因為進行了事件攔截,所以執行完onInterceptTouchEvent後不再向下傳遞,而是直接交由MyViewGroupB的onTouchEvent進行消費處理。由於我們是隻攔截,不消費,所以事件向上傳遞,交由上層處理,最終回到Activity。之後觸發的ACTION_MOVE和ACTION_UP也不再向下傳遞,直接交由Activity分發給自己處理。

3、我們還是將MyViewGroupB的onInterceptTouchEvent返回super,但是將他的onTouchEvent返回true。這意味著我們不攔截,但是由MyViewGroupB進行事件處理。

輸出結果如下:

I/Activity: dispatchTouchEvent_ACTION_DOWN


I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN


I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN


I/MyView: dispatchTouchEvent_ACTION_DOWN
I/MyView: onTouchEvent_ACTION_DOWN
I/MyViewGroupB: onTouchEvent_ACTION_DOWN


I/Activity: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupA: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVE
I/MyViewGroupB: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupB: onTouchEvent_ACTION_MOVE


I/Activity: dispatchTouchEvent_ACTION_UP
I/MyViewGroupA: dispatchTouchEvent_ACTION_UP
I/MyViewGroupA: onInterceptTouchEvent_ACTION_UP
I/MyViewGroupB: dispatchTouchEvent_ACTION_UP
I/MyViewGroupB: onTouchEvent_ACTION_UP

結合輸出結果,總結如下:
 

可以看出,當觸發ACTION_DOWN的時候,事件的分發傳遞過程和1的時候一樣,從Activity開始一層層向下傳遞,最終傳遞到最底層MyView,觸發消費操作,然後MyView將消費操作移交上層處理,然後到達MyViewGroupB的onTouchEvent,並且進行了消費處理,事件處理到此不在向上移交。當觸發ACTION_MOVE和ACTION_UP操作時,事件依然需要由Activity開始向下分發傳遞,但是當傳遞到MyViewGroupB後,由於其消費了ACTION_DOWN,事件將不再繼續向下分發,而是直接由MyViewGroupB分發給自己的onTouchEvent進行繼續處理。事件處理也不再向上移交。

4、將MyViewGroupB的onInterceptTouchEvent和onTouchEvent的返回值均改為true。這意味著既攔截,又消費。

輸出結果如下:

I/Activity: dispatchTouchEvent_ACTION_DOWN


I/MyViewGroupA: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupA: onInterceptTouchEvent_ACTION_DOWN


I/MyViewGroupB: dispatchTouchEvent_ACTION_DOWN
I/MyViewGroupB: onInterceptTouchEvent_ACTION_DOWN


I/MyViewGroupB: onTouchEvent_ACTION_DOWN


I/Activity: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupA: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupA: onInterceptTouchEvent_ACTION_MOVE
I/MyViewGroupB: dispatchTouchEvent_ACTION_MOVE
I/MyViewGroupB: onTouchEvent_ACTION_MOVE


I/Activity: dispatchTouchEvent_ACTION_UP
I/MyViewGroupA: dispatchTouchEvent_ACTION_UP
I/MyViewGroupA: onInterceptTouchEvent_ACTION_UP
I/MyViewGroupB: dispatchTouchEvent_ACTION_UP
I/MyViewGroupB: onTouchEvent_ACTION_UP

結合輸出結果,總結如下:
 

當觸發ACTION_DOWN的時候,依然從Activity開始向下傳遞,當到達MyViewGroupB的是,因為在onInterceptTouchEvent進行了攔截操作,因此不再繼續向下分發傳遞,而是交由MyViewGroupB的onTouchEvent進行處理消費。MyViewGroupB的onTouchEvent返回的是true,說明它決定對ACTION_DOWN進行處理,因此事件也就不再移交上層處理。當觸發ACTION_MOVE和ACTION_UP的時候,事件還是從Activity開始向下傳遞,當到達MyViewGroupB的時候,由於之前進行了攔截操作,因此,MyViewGroupB直接將事件分發給自己的onTouchEvent進行處理,不在向下分發傳遞。事件處理也不再向上層移交。