Android事件分發機制解析

juexingzhe發表於2018-01-02

部落格地址juexingzhe歡迎關注

今天結合流程圖和程式碼來對Android事件分發機制做一個總結,我自己起一個叫法就是“3個3”。

跟事件分發相關的主要有三個節點方法:

1.dispatchTouchEvent 2.onInterceptTouchEvent(這個只有ViewGroup有) 3.onTouchEvent

為了簡單扼要,Demo總共就三個研究物件,Activity/ViewGroup/View,從屬關係就是Activity中載入ViewGroup, ViewGroup中有一個View是Button,之所以用Button就是為了點選事件。在Button點選的時候看下三者事件分發的順序。

在Activity中, 主要就是新增幾個Log,在onTouchEvent中列印出MotionEvent事件,這裡為了簡單主要關注

1.ACTION_DOWN 按下事件 2.ACTION_MOVE 移動事件 3.ACTION_UP 鬆開事件

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.w(Constants.TAG, "------------------------------------------");
        Log.d(Constants.TAG, "MainActivity.dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        String eventString;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                eventString = "ACTION_DOWN";
                break;
            case MotionEvent.ACTION_MOVE:
                eventString = "ACTION_MOVE";
                break;
            case MotionEvent.ACTION_UP:
                eventString = "ACTION_UP";
                break;
            default:
                eventString = "OTHER_EVENT";
                break;
        }
        Log.d(Constants.TAG, "MainActivity.onTouchEvent: " + eventString);
        return super.onTouchEvent(event);
    }
複製程式碼

在ViewGroup中,多了一個onInterceptTouchEvent方法。

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        Log.d(Constants.TAG, "ViewGroupCustom.dispatchTouchEvent");
        return super.dispatchTouchEvent(ev);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        Log.d(Constants.TAG, "ViewGroupCustom.onInterceptTouchEvent");
        return true;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        String eventString;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                eventString = "ACTION_DOWN";
                break;
            case MotionEvent.ACTION_MOVE:
                eventString = "ACTION_MOVE";
                break;
            case MotionEvent.ACTION_UP:
                eventString = "ACTION_UP";
                break;
            default:
                eventString = "OTHER_EVENT";
                break;
        }
        Log.d(Constants.TAG, "ViewGroupCustom.onTouchEvent: " + eventString);
        return true;
    }
複製程式碼

在View中方法和Activity中一樣:

    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        Log.d(Constants.TAG, "ViewCustom.dispatchTouchEvent");
        return super.dispatchTouchEvent(event);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        String eventString;
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                eventString = "ACTION_DOWN";
                break;
            case MotionEvent.ACTION_MOVE:
                eventString = "ACTION_MOVE";
                break;
            case MotionEvent.ACTION_UP:
                eventString = "ACTION_UP";
                break;
            default:
                eventString = "OTHER_EVENT";
                break;
        }
        Log.d(Constants.TAG, "ViewCustom.onTouchEvent: " + eventString);
        return super.onTouchEvent(event);
    }
複製程式碼

預設三個方法都是呼叫super的方法。

稍微總結下,涉及到三個3

1.第一個3就是三個研究物件:Activity/ViewGroup/View 2.第二個3就是三個方法:dispatchTouchEvent/onInterceptTouchEvent/onTouchEvent 3.第二個3就是每個方法都return3中狀態之一:super/true/false

接下來就是排列組合,事件分發就是在三個物件,三個方法,三個狀態之間進行各種排列組合,就組合成事件分發的多種狀態。接下來上一個很重要的圖,就是整體的事件分發流程圖:

EventDispatch.png

上面這張流程圖很重要,今天這個解析就是圍繞著它轉了。下面就是分別把上面不同狀態進行詳細解釋。

先做個說明,在按鈕點選的時候,我們就關注三個事件,ACTION_DOWN/ACTION_MOVE/ACTION_UP。

首先是三個物件的三個方法都不做更改,也就是都呼叫super,這就是預設狀態。這樣三個事件都會到按鈕的onTouchEvent,並且呼叫onClick回撥方法。

Default.PNG

接下來開始做點手腳,在Activity的dispatchTouchEvent中返回true狀態,這樣Activity就消費掉事件,不再往下傳給另外兩個物件,甚至也不呼叫自己的onTouchEvent方法。

activity_dispatch_true.PNG

Activity的dispatchTouchEvent中返回false,也是自己消費點,和上面返回true是一樣的。

activity_dispatch_false.PNG

接下來activity兩個方法保持預設,也就是super狀態,這樣事件就能傳遞到viewgroup了。然後對viewgroup動手腳,viewgroup有三個方法,所以狀態會多一點。首先在第一個方法dispatchTouchEvent返回true,這樣和acticity一樣的,自己直接消費掉,也不給自己的onTouchEvent。

viewgroup_dispatch_true.PNG

如果把狀態改成false呢?這種情況也就類似於員工反了不幹了,只能領導自己幹,事件就會給activity的onTouchEvent消費,再之後的move和up事件不會再分發了,activity直接給自己的onTouchEvent消費。

viewgroup_dispatch_false.PNG

接下來就是viewgroup的第二個方法onInterceptTouchEvent了。如果第一個方法預設返回super狀態,那麼就會把事件給這個方法,viewgroup通過這個方法來告訴系統攔不攔截這個時間。返回true就是攔截,事件就會給自己的第三個方法onTouchEvent消費。如果onTouchEvent返回super或者false,那麼事件就會給父類activity消費。之後事件不再傳給viewgroup,activity自己直接消費。這就類似於老闆交給員工任務,員工沒完成好,老闆以後就不交給這個員工了。

viewgroup_onIntercept_true.PNG

如果viewgroup的第二個方法返回false,表示自己不做攔截,那麼事件就會傳遞給子類,這裡就是button了。button就預設給自己的onTouchEvent消費掉。

viewgroup_onIntercept_false.PNG

如果第二個方法返回true表示攔截,事件就會給自己的onTouchEvent消費,onTouchEvent返回true,事件就是viewgroup自己消費,後續的事件也會給到viewgroup。

viewgroup_onIntercept_true_onTouch_true.PNG

如果onTouchEvent返回false,事件就會給父類activity消費。之後事件不再傳給viewgroup,activity自己直接消費。

viewgroup_onIntercept_true_onTouch_false.PNG

最後就是最後一個物件view,在這裡是button。view只有兩個方法,沒有onInterceptTouchEvent。首先如果dispatchTouchEvent返回true,那麼事件就直接消費掉了,不傳遞給自己的onTouchEvent方法。

view_dispatch_true.PNG

dispatchTouchEvent返回false就會把事件給父類的onTouchEvent消費。以後事件不再交給這個view。

view_dispatch_false.PNG

view的onTouchEvent方法如果返回true,那麼事件就會自己消費點,並且不會呼叫onClick這個回撥方法。

view_onTouch_true.PNG

如果onTouchEvent返回false,那麼事件就會交給父類,這個系列剩下的事件就不會再交給這個view了。

view_onTouch_false.PNG

到這裡事件分發就說的差不多了,我們這個Demo比較簡單,但是不影響理解原理。簡單坐下總結:

1.對於dispatchTouchEvent這個方法,返回true都是直接消費掉,不做其他傳遞。返回false就有點區別,對於activity是和true一樣直接消費掉,對於viewgroup和view就是把事件給父類的onTouchEvent消費。返回super就都是進行分發 2.onInterceptTouchEvent這個方法只有viewgroup有,返回true就是攔截,會把事件給到自己的onTouchEvent消費;返回false和返回super是一樣的,不攔截,分發給子view 3.onTouchEvent返回true就是消費掉事件了,如果返回false就傳遞給父類。返回super有點區別,對於viewgroup就和false一樣,傳遞給父類;對於view就會再接著往下傳遞,比如呼叫點選回撥等。

到這裡就把事件分發說的差不多了,沒有放上原始碼分析,我是覺得那樣內容就有點多,容易亂,如果面試的時候畫出上面的流程圖就差不多了。

如果本文對你有幫助,請點個贊哈,謝謝!

相關文章