Android事件分發機制解析

博客地址juexingzhe歡迎關注面試

今天結合流程圖和代碼來對Android事件分發機制作一個總結,我本身起一個叫法就是「3個3」。bash

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

1.dispatchTouchEvent 2.onInterceptTouchEvent(這個只有ViewGroup有) 3.onTouchEvent源碼分析

爲了簡單扼要,Demo總共就三個研究對象,Activity/ViewGroup/View,從屬關係就是Activity中加載ViewGroup, ViewGroup中有一個View是Button,之因此用Button就是爲了點擊事件。在Button點擊的時候看下三者事件分發的順序。ui

在Activity中, 主要就是添加幾個Log,在onTouchEvent中打印出MotionEvent事件,這裏爲了簡單主要關注spa

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

@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方法。code

@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中同樣:cdn

@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就會再接着往下傳遞,好比調用點擊回調等。

到這裏就把事件分發說的差很少了,沒有放上源碼分析,我是以爲那樣內容就有點多,容易亂,若是面試的時候畫出上面的流程圖就差很少了。

若是本文對你有幫助,請點個贊哈,謝謝!

相關文章
相關標籤/搜索