天天看點

Android事件分發機制流程詳解(一)

前言:今天來捋一捋Android的事件分發機制吧,其實很多人都知道有這麼個東西,但是具體的事件執行流程沒有認真的研究過分析過的,還是很難捋清楚其中的關系的,畢竟從activity到ViewGroup到View,有那麼多的dispatchTouchEvent、onTouchEvent,ViewGroup中還多了個onInterceptTouchEvent方法,方法都可以傳回true、false或者super.方法,是以涉及到的情況就比較多了,不研究清楚的話很容易說不上來事件會怎麼執行,但是一旦你弄清楚了,以後保證你絕對忘不了了,雖然網上有很多的教程,但是那是别人的,與其讓我去了解吸收他們的思想和邏輯,不如自己好好整理一下寫下來,以後這也是自己的一個複習資料,是以各位看官,不喜勿噴哈~

首先我們要明白一件事,那就是Android中事件分發順序:Activity(Window) -> ViewGroup -> View,貼出下圖:

Android事件分發機制流程詳解(一)

其中:

super:調用父類方法

true:消費事件,即事件不繼續往下傳遞

false:不消費事件,事件繼續往下傳遞 / 交由給父控件onTouchEvent()處理

那麼這三個方法都是在什麼時候會被調用呢?看看下面這張圖:

Android事件分發機制流程詳解(一)

下面我們将一步步的驗證這個邏輯。

一、寫程式、分析流程、驗證結果

1.1 建立工程初始化代碼

1.1.1 在MainActivity中添加初始代碼

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);
        view = findViewById(R.id.myview);

        view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                System.out.println("觸發了點選事件");
            }
        });
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.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 false;
    }

    @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 false;
    }
}
           

1.1.2建立一個MyRelativeLayout繼承RelativeLayout,初始代碼如下

public class MyRelativeLayout extends RelativeLayout {


    private static final String TAG = "MyRelativeLayout";

    public MyRelativeLayout(Context context) {
        super(context);
    }

    public MyRelativeLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyRelativeLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        switch (ev.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 false;
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        switch (ev.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 false;
    }

    @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 false;
    }
}
           

1.1.3 建立一個MyView繼承View,初始代碼如下

public class MyView extends View {

    private static final String TAG = "MyView";

    public MyView(Context context) {
        super(context);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyView(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 false;
    }

    @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 false;
    }
}
           

1.1.4 activity_main.xml中引入上面的兩個自定義控件

<?xml version="1.0" encoding="utf-8"?>
<com.zy.toucheventdemo.MyRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/activity_touch_event"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.zy.toucheventdemo.MyView
        android:id="@+id/myview"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerInParent="true"
        android:background="#ff3839" />

</com.zy.toucheventdemo.MyRelativeLayout>
           

1.2 修改條件、分析流程、驗證結果

在這個步驟裡我們依次修改各方法return的傳回值,然後在程式運作之前分析一下執行順序,然後運作程式驗證我們的分析。

測試1:

條件:

MainActivity:

dispatchTouchEvent:return false;

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return false;

onInterceptTouchEvent:return false;

onTouchEvent:return false;

MyView:

dispatchTouchEvent:return false;

onTouchEvent:return false;

所有類中的dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent方法都return false,結果是什麼?

分析:根據文首的圖檔分析可知,都傳回false的時候,事件在進入到MainActivity的dispatchTouchEvent方法時,事件并不會繼續往下傳遞,而是直接消費掉了,是以應該執行的是MainActivity的dispatchTouchEvent()方法中的ACTION_DOWN、ACTION_MOVE、ACTION_UP三個方法,執行代碼看看輸出結果:

Android事件分發機制流程詳解(一)

測試2:

條件:

MainActivity:

dispatchTouchEvent:return true;

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return false;

onInterceptTouchEvent:return false;

onTouchEvent:return false;

MyView:

dispatchTouchEvent:return false;

onTouchEvent:return false;

MainActivity中的dispatchTouchEvent方法return true;結果是什麼?

分析:當修改MainActivity中的dispatchTouchEvent方法傳回true時,事件被消費掉了,也不會往下傳遞,這個時候輸出結果應該和測試1中的結果一樣。

Android事件分發機制流程詳解(一)

小結:

到這裡我們可以看到,不管Activity中的dispatchTouchEvent return true還是false;事件都被消費了,不會往下傳遞,與流程圖所示一緻。

測試3:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return false;

onInterceptTouchEvent:return false;

onTouchEvent:return false;

MyView:

dispatchTouchEvent:return false;

onTouchEvent:return false;

MainActivity中的dispatchTouchEvent方法return super.dispatchTouchEvent(ev);結果是什麼?

分析:當MainActivity中的dispatchTouchEvent方法return super.dispatchTouchEvent(ev);說明這個時候會調用父類方法,事件向ViewGroup中傳遞,這個時候事件就會傳遞到MyRelativeLayout的dispatchTouchEvent方法中,由于MyRelativeLayout的dispatchTouchEvent方法是return false,表示事件将交由給父控件onTouchEvent方法處理,即MainActivity的onTouchEvent()方法會響應事件,這個時候不論MainActivity的onTouchEvent()方法return true或者return false;事件都會被消費。

Android事件分發機制流程詳解(一)

測試4:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return true;

onInterceptTouchEvent:return false;

onTouchEvent:return false;

MyView:

dispatchTouchEvent:return false;

onTouchEvent:return false;

MainActivity中的dispatchTouchEvent方法return super.dispatchTouchEvent(ev);MyRelativeLayout中的dispatchTouchEvent方法return true;結果是什麼?

分析:接上,MainActivity會向MyRelativeLayout傳遞事件,并且MyRelativeLayout中的dispatchTouchEvent方法return true;是以到這裡,事件就會被消費掉,我們這裡分析的流程應該如下:

MainActivity:dispatchTouchEvent ACTION_DOWN

MyRelativeLayout:dispatchTouchEvent ACTION_DOWN

MainActivity:dispatchTouchEvent ACTION_MOVE

MyRelativeLayout: dispatchTouchEvent ACTION_MOVE

MainActivity:dispatchTouchEvent ACTION_UP

MyRelativeLayout: dispatchTouchEvent ACTION_UP

Android事件分發機制流程詳解(一)

測試5:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onInterceptTouchEvent:return false;

onTouchEvent:return false;

MyView:

dispatchTouchEvent:return false;

onTouchEvent:return false;

接上,這個時候将MyRelativeLayout中dispatchTouchEvent方法return super.dispatchTouchEvent(ev);傳回結果是什麼?

分析:在之前測試的基礎之上,将MyRelativeLayout中dispatchTouchEvent方法return super.dispatchTouchEvent(ev);那麼事件将會繼續向下傳遞到onInterceptTouchEvent()方法中,這個方法主要是用來判斷是否攔截該事件,我們一開始預設的是return false;是以并不會攔截,事件将會繼續向下傳遞到MyView的dispatchTouchEvent()方法中,由于MyView的dispatchTouchEvent()方法預設也是return false;是以MyView不會處理該事件,事件交由父類MyRelativeLayout的onTouchEvent()方法執行,是以我們分析的執行流程應該是:

MainActivity: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout:dispatchTouchEvent ACTION_DOWN

MyRelativeLayout:onInterceptTouchEvent ACTION_DOWN

MyView:dispatchTouchEvent ACTION_DOWN

MyRelativeLayout:onTouchEvent ACTION_DOWN

MainActivity: onTouchEvent ACTION_DOWN

到這裡要注意了,事件已經從MyView傳遞到MyRelativeLayout又傳遞到MainActivity中了,是以接下來的事件都是在MainActivity中進行

MainActivity: dispatchTouchEvent ACTION_MOVE

MainActivity: onTouchEvent ACTION_MOVE

MainActivity: dispatchTouchEvent ACTION_UP

MainActivity: onTouchEvent ACTION_UP

Android事件分發機制流程詳解(一)

測試6:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onInterceptTouchEvent:return true;

onTouchEvent:return false;

MyView:

dispatchTouchEvent:return false;

onTouchEvent:return false;

接上,将MyRelativeLayout中的onInterceptTouchEvent()方法中修改為return true;結果是什麼?

分析:其他步驟不變,當事件傳到onInterceptTouchEvent()方法中,return true;表示攔截此事件,那麼事件就會傳遞到onTouchEvent()方法中,由于onTouchEvent()方法return false;那麼事件又會傳遞到MainActivity的onTouchEvent()方法中,是以我們的邏輯應該是這樣的:

MainActivity: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN

MyRelativeLayout:onTouchEvent ACTION_DOWN

MainActivity: onTouchEvent ACTION_DOWN

MainActivity: dispatchTouchEvent ACTION_MOVE

MainActivity: onTouchEvent ACTION_MOVE

MainActivity: dispatchTouchEvent ACTION_UP

MainActivity: onTouchEvent ACTION_UP

Android事件分發機制流程詳解(一)

測試7:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onInterceptTouchEvent:return true;

onTouchEvent:return true;

MyView:

dispatchTouchEvent:return false;

onTouchEvent:return false;

接上,現在将MyRelativeLayout中onTouchEvent()方法return true;結果是什麼?

分析:前面步驟不變,當事件傳遞到MyRelativeLayout中onTouchEvent()方法時,這個時候return true;表示事件将會被消費,是以我們的邏輯應該是這樣的:

MainActivity: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN

MyRelativeLayout: onTouchEvent ACTION_DOWN

注意:剩下的事件将從Activity->ViewGroup依次進行

MainActivity:dispatchTouchEvent ACTION_MOVE

MyRelativeLayout:dispatchTouchEvent ACTION_MOVE

這個時候就不會走onInterceptTouchEvent方法了

MyRelativeLayout:onTouchEvent ACTION_MOVE

MainActivity:dispatchTouchEvent ACTION_UP

MyRelativeLayout:dispatchTouchEvent ACTION_UP

MyRelativeLayout:onTouchEvent ACTION_UP

Android事件分發機制流程詳解(一)

測試8:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);

onTouchEvent:return true;

MyView:

dispatchTouchEvent:return false;

onTouchEvent:return false;

接上,現在将MyRelativeLayout中的onInterceptTouchEvent()方法改為return super.onInterceptTouchEvent(ev);結果是什麼?

分析:前面步驟不變,将MyRelativeLayout中的onInterceptTouchEvent()方法改為return super.onInterceptTouchEvent(ev);表示不攔截事件,事件将會繼續往下傳遞到MyView的dispatchTouchEvent()方法中,由于MyView的dispatchTouchEvent()方法 return false;後續的流程和測試5一樣,是以onInterceptTouchEvent()方法return super.onInterceptTouchEvent(ev);或者return false;效果一樣,和首圖表示一緻。

測試9:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);

onTouchEvent:return true;

MyView:

dispatchTouchEvent:return true;

onTouchEvent:return false;

接上,現在将MyView中dispatchTouchEvent()方法修改為return true;結果是什麼?

分析:從測試8中我們知道,由于MyView中dispatchTouchEvent()方法return false;導緻事件向上傳遞,那麼這麼修改為return true;表示事件将會被消費掉,是以我們可以得出一下邏輯:

MainActivity: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN

MyView: dispatchTouchEvent ACTION_DOWN

這裡return true;事件會被消費,不向下傳遞了

MainActivity: dispatchTouchEvent ACTION_MOVE

MyRelativeLayout: dispatchTouchEvent ACTION_MOVE

注意,由于onInterceptTouchEvent return super.onInterceptTouchEvent(ev)/false;View的dispatchTouchEvent消費事件時,onInterceptTouchEvent方法的ACTION_MOVE會執行

MyRelativeLayout: onInterceptTouchEvent ACTION_MOVE

MyView: dispatchTouchEvent ACTION_MOVE

MainActivity: dispatchTouchEvent ACTION_UP

MyRelativeLayout: dispatchTouchEvent ACTION_UP

MyRelativeLayout: onInterceptTouchEvent ACTION_UP

MyView: dispatchTouchEvent ACTION_UP

Android事件分發機制流程詳解(一)

測試10:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);

onTouchEvent:return true;

MyView:

dispatchTouchEvent:return super.dispatchTouchEvent(event);

onTouchEvent:return false;

接上,現在将MyView中dispatchTouchEvent()方法修改為return super.dispatchTouchEvent(event);結果是什麼?

分析:前面步驟相同,當MyView中dispatchTouchEvent()方法修改為return super.dispatchTouchEvent(event);可以知道事件将會往下傳遞到onTouchEvent方法中,預設是return false;是以事件又會被傳遞到MyRelativeLayout的onTouchEvent方法中去,這個時候MyRelativeLayout的onTouchEvent方法是return true;是以這裡我們就分析這一種情況,return false的情況大家自己分析即可,是以我們得到的邏輯應該是這樣的:

MainActivity: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN

MyView: dispatchTouchEvent ACTION_DOWN

MyView:onTouchEvent ACTION_DOWN

由于是return false;事件向上傳遞到MyRelativeLayout的onTouchEvent方法中

MyRelativeLayout:onTouchEvent ACTION_DOWN

MainActivity:dispatchTouchEvent ACTION_MOVE

MyRelativeLayout:dispatchTouchEvent ACTION_MOVE

由于MyView中的dispatchTouchEvent 不消費事件,是以onInterceptTouchEvent 的後續不會執行

MyRelativeLayout:onTouchEvent ACTION_MOVE

MainActivity:dispatchTouchEvent ACTION_UP

MyRelativeLayout:dispatchTouchEvent ACTION_UP

MyRelativeLayout:onTouchEvent ACTION_UP

Android事件分發機制流程詳解(一)

測試11:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);

onTouchEvent:return true;

MyView:

dispatchTouchEvent:return super.dispatchTouchEvent(event);

onTouchEvent:return true;

接上,現在将MyView的onTouchEvent修改為return true;結果是什麼?

分析:事件會一路傳遞到MyView中,并且有onTouchEvent接收并消費掉,是以我們分析一下邏輯應該是:

MainActivity: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN

MyView: dispatchTouchEvent ACTION_DOWN

MyView: onTouchEvent ACTION_DOWN

MainActivity: dispatchTouchEvent ACTION_MOVE

MyRelativeLayout: dispatchTouchEvent ACTION_MOVE

MyRelativeLayout: onInterceptTouchEvent ACTION_MOVE

MyView:dispatchTouchEvent ACTION_MOVE

MyView:onTouchEvent ACTION_MOVE

MainActivity: dispatchTouchEvent ACTION_UP

MyRelativeLayout: dispatchTouchEvent ACTION_UP

MyRelativeLayout: onInterceptTouchEvent ACTION_UP

MyView:dispatchTouchEvent ACTION_UP

MyView:onTouchEvent ACTION_UP

Android事件分發機制流程詳解(一)

總結

這裡我們對ViewGroup的onInterceptTouchEvent()方法做一個小結,結合上述幾個測試的結果可以分析得到:

  1. 當onInterceptTouchEvent()方法return true;時表示攔截該事件交由onTouchEvent()方法執行,以後的onInterceptTouchEvent()方法的move,up方法都不會執行了。
  2. 如果事件交由ViewGroup的子View并且消費了的話,那麼ViewGroup的onInterceptTouchEvent()方法還是會依次執行move,up方法。
  3. 如果事件傳遞到子View,但是子View不處理向上傳回到ViewGroup的onTouchEvent()方法中,那麼onInterceptTouchEvent()方法的move,up方法都不會執行。

由此可見,隻有當事件從ViewGroup傳遞到子View并且消費了事件的話,ViewGroup的onInterceptTouchEvent()方法中的move,up方法才會執行。

如果大家上面的所有測試方法都能看懂并且在不看運作結果的情況下,根據條件所給的傳回值,能夠正确的分析出執行流程,那麼恭喜你,你已經了解了事件分發的機制,我們晚點再講源碼部分,但是不知道大家有沒有發現一個問題,為什麼我在Activity中給view.setOnClickListener中的onClick回調方法并沒有執行呢?事件好像就消失不見了,這裡大家要注意下了,onClick方法沒有執行是因為MyView中的onTouchEvent方法應該是return super.onTouchEvent(event);這樣事件才能交由onClick方法執行,不信我們可以看看測試12的結果。

測試12:

條件:

MainActivity:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onTouchEvent:return false;

MyRelativeLayout:

dispatchTouchEvent:return super.dispatchTouchEvent(ev);

onInterceptTouchEvent:return super.onInterceptTouchEvent(ev);

onTouchEvent:return true;

MyView:

dispatchTouchEvent:return super.dispatchTouchEvent(event);

onTouchEvent:return super.onTouchEvent(event);

分析:我們主要分析一下事件的執行流程,從MainActivity->MyRelativeLayout->MyView->onClick();我們來寫一寫流程:

MainActivity: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: dispatchTouchEvent ACTION_DOWN

MyRelativeLayout: onInterceptTouchEvent ACTION_DOWN

MyView: dispatchTouchEvent ACTION_DOWN

MyView: onTouchEvent ACTION_DOWN

MainActivity: dispatchTouchEvent ACTION_MOVE

MyRelativeLayout: dispatchTouchEvent ACTION_MOVE

MyRelativeLayout: onInterceptTouchEvent ACTION_MOVE

MyView: dispatchTouchEvent ACTION_MOVE

MyView: onTouchEvent ACTION_MOVE

MainActivity: dispatchTouchEvent ACTION_UP

MyRelativeLayout: dispatchTouchEvent ACTION_UP

MyRelativeLayout: onInterceptTouchEvent ACTION_UP

MyView: dispatchTouchEvent ACTION_UP

MyView: onTouchEvent ACTION_UP

onClick:觸發了點選事件

Android事件分發機制流程詳解(一)

可以看到,事件正如我們所分析的一樣往下執行,當所有的down,move,up方法執行完後就會執行最後的onClick方法。

其實當控件被點選時,在View的onTouchEvent()方法執行之前,還有另一個方法會優先執行,這個方法就是onTouch()方法,在View中無法重寫這個方法,隻能通過在給view.setOnTouchListener(new View.OnTouchListener() {});的形式,才能執行onTouch()方法,下面我們在MainActivity中給view添加一個onTouch事件監聽器并列印日志

view.setOnTouchListener(new View.OnTouchListener() {
  @Override
    public boolean onTouch(View v, MotionEvent event) {
        System.out.println("執行了onTouch(), 動作是:" + event.getAction());
        return false;
    }
});
           

這裡預設就是return false;

之前我們已經說過了,onTouch方法會優先于onTouchEvent方法執行,是以我們來分析一下這裡傳回的true或者false對于事件分發有什麼影響?

當onTouch方法return true;時,事件執行到這兒的時候,應該就被消費掉了,onTouchEvent方法就不會繼續執行,是以我們的onClick方法就不會被回調。

當onTouch方法return false;時,事件不會被消費,交由onTouchEvent方法執行,根據onTouchEvent方法的傳回值決定onClick方法是否執行。

我們來試一試,看看兩者的差別。

測試13:

  • 當onTouch return false;
    Android事件分發機制流程詳解(一)

可以看到onTouch方法确實是在onTouchEvent方法之前執行,這裡的動作0代表down,1代表up,2代表的是move。并且當onTouch傳回false的時候,和我們分析的一樣,事件不攔截,繼續往下傳遞,最終回調了onClick方法。

  • 當onTouch return true;
    Android事件分發機制流程詳解(一)

可以看到,當我們的事件執行到onTouch時,因為被消費了,是以并不會繼續往下走,當執行完ACTION_UP後,事件就被消費完了,onClick并不會被回調。

注意: 也許會有人問,如果onTouch return的是super.onTouch(event)呢?那事件會怎麼執行?哈哈哈,不好意思,并沒有super.onTouch(event)這個方法,是以onTouch方法隻能是return true;或者return false;

由于篇幅過長,這一篇咱們就先通過分析和測試捋清楚Android事件分發的過程和邏輯,如果大家能看懂,相信大家已經離真相不遠了,具體的源碼分析咱們就留到下一篇再來分析吧!

繼續閱讀