Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android 的事件傳遞機制,詳細解釋

Android 的事件傳遞機制,詳細解釋

編輯:關於Android編程

Android 的事件傳遞機制,詳細解釋

前兩天和一個朋友聊天的時候,然後說到事件傳遞機制,然後讓我說的時候,忽然發現說的不是很清楚,其實Android 的事件傳遞機制也是知道一些,但是感覺自己知道的很模糊,僅僅是知道事件是從外層先傳到內層,在從內存最後回饋到外層,但是詳細的幾個方法的調用過程,自己卻知道的不是很詳細,我想很多人都是這種情況,然後自己就上網去查,然後看到的全部都是在講會調用哪個幾個方法,但是基本沒有講這幾個方法的具體作用。自己回去寫了demo ,今天就把自己的理解寫出來,希望大家能更詳細了解這幾個方法的具體作用。

首先事件傳遞會用到的幾個方法,我們先列舉一下。

public boolean onInterceptTouchEvent(MotionEvent ev) public boolean dispatchTouchEvent(MotionEvent ev) public boolean onTouchEvent(MotionEvent ev)

我們查看源碼,可以看到,onInterceptTouchEvent 這個方法是viewGroup 的方法

但是 dispatchTouchEvent 和 onTouchEvent 都是view 方法。

那麼我們先寫一個View OutView(外層view)繼承ViewGroup ,MiddleView(中層View) 繼承ViewGroup ,InnerView (內層Button,Button 也是繼承的view )繼承View

既然 dispatchTouchEvent 和 onTouchEvent 是view 的方法,那麼我們在每個view 裡面重寫這兩個方法,如下,

每個方法會打印兩邊log,第一個log,是調用這個方法開始的時候,就打一句,第二個log ,是這個方法調用父類,也就說,該走的邏輯走完,並且有返回值的時候。為了log 清晰,都只是打印了 ACTION_DOWN 事件的。

 @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, dispatchTouchEvent);
                break;
        }

        boolean flag = super.dispatchTouchEvent(ev);

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, dispatchTouchEvent: + flag);
                break;
        }


        return flag;
    }


    @Override
    public boolean onTouchEvent(MotionEvent ev) {


        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onTouchEvent);
                break;
        }

        boolean flag = super.onTouchEvent(ev);

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onTouchEvent: + flag);
                break;
        }


        return flag;


    }

輸出的log 如下

toucheventdemo E/OutView﹕ dispatchTouchEvent
toucheventdemo E/MiddleView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent:true
toucheventdemo E/InnerView﹕ dispatchTouchEvent:true
toucheventdemo E/MiddleView﹕ dispatchTouchEvent:true
toucheventdemo E/OutView﹕ dispatchTouchEvent:true

可以看到前三個雖然都是直接都是dispatchTouchEvent 方法裡面的log 打印的,但是log並沒有返回值,也就是說,都是執行的方法裡面第一句log,
但是當執行到InnerView﹕ dispatchTouchEvent 的時候,又執行了InnerView﹕ onTouchEvent 的方法

其實是這樣的, boolean flag = super.dispatchTouchEvent; 這一句,super 中,執行的dispatchTouchEvent ,
其實會做兩件事

1,第一件事,是看當前的view 有沒有子view,如果有子view,並且會判斷當前的觸摸點是否在子view 上面,如果在的話,那麼就會調用子view的dispatchTouchEvent  ,然後一層一層往裡面調用,
2.一直調用到沒有子view 的時候,然後停止dispatchTouchEvent ,因為InnerView 是繼承的view,當然沒有子view,然後這個時候,就開始調用子view 的onTouchEvent。

onTouchEvent 返回 true ,那麼當前view 的dispatchTouchEvent  也會返回view, 此時,就會講dispatchTouchEvent 的分發結果反饋給父view,當前view 的父view 的dispatchTouchEvent 也會返回true,以此類推,返回到最外層,

那麼如果裡層的view 返回的true 那如果最裡層的view onTouchEvent返回的是false 那?我們將裡層的view 繼承改成View,

toucheventdemo E/OutView﹕ dispatchTouchEvent
toucheventdemo E/MiddleView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent:false
toucheventdemo E/InnerView﹕ dispatchTouchEvent:false
toucheventdemo E/MiddleView﹕ onTouchEvent
toucheventdemo E/MiddleView﹕ onTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent:false
toucheventdemo E/OutView﹕ onTouchEvent
toucheventdemo E/OutView﹕ onTouchEvent:false
toucheventdemo E/OutView﹕ dispatchTouchEvent:false

我們會發現,當裡層的view ,onTouchEvent:false 的時候,那麼裡層的dispatchTouchEvent 也是false ,並且此時立馬會調用,裡層view 的父view(也就是MiddleView) 的 onTouchEvent 事件,MiddleView的onTouchEvent 也返回false ,那麼此時MiddleView dispatchTouchEvent 也會是false,然後接著調用了最外層 OutView onTouchEvent 方法。

從這兩個小試驗,你能看出,dispatchTouchEvent 的返回結果是怎麼得來的嗎?

分兩種情況

當前 沒有子view 那麼當前view 的 dispatchTouchEvent 結果就是當前view onTouchEvent 的結果

當前view 有子view, 此時又分兩種情況

子view 的 dispatchTouchEvent 返回false 那麼當前view 的 dispatchTouchEvent 結果參照第一種情況,也就是當前view onTouchEvent 執行 ,並且dispatchTouchEvent 的結果是 當前view 的 onTouchEvent 的結果 子view 的 onTouchEvent 返回true ,那麼 此時當前view 的onTouchEvent 的不執行 直接直接返回的是子view 的 dispatchTouchEvent 返回的view
將兩種情況再總結一下,就是,當前view的 dispatchTouchEvent 的返回結果是:如果子view 的 dispatchTouchEvent 返回true ,那麼當前view 不執行onTouchEvent ,當前view 的 dispatchTouchEvent 直接返回true, 如果子view 的dispatchTouchEvent 返回false ,當前view 的 dispatchTouchEvent的結果看 看當前view 的onTouchEvent 結果。

看到這裡,你是否對, dispatchTouchEvent onTouchEvent 稍微有一些理解了那?其實我們看方法的名稱也應該能理解一些,dispatchTouchEvent 翻譯:觸摸事件分發, onTouchEvent 觸摸事件,也就是說dispatchTouchEvent 主要做的是處理的觸摸事件的分發管理,用來告訴父view 當前view 有沒有處理我們的觸摸事件,並且處理,當前觸摸的點,還有沒有子view, onTouchEvent 用來處理我們具體的觸摸事件。

(具體這裡為什麼說還要判斷當前觸摸的點還有沒有子view,你可以自己試驗,觸摸MiddleView 之內,InnerView之外的區域,看會不會打印InnerView 裡面的log )

說了半天,我們一直在說dispatchTouchEvent onTouchEvent 這兩個方法,那麼onInterceptTouchEvent 這個方法又是干嘛的那?

首先翻譯api onInterceptTouchEvent : 攔截TouchEvent 事件。

應為onInterceptTouchEvent 是viewGroup 的方法,那麼我們重寫OutView 和 MiddleView 的onInterceptTouchEvent方法


    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onInterceptTouchEvent);
                break;
        }

        boolean flag = super.onInterceptTouchEvent(ev);


        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onInterceptTouchEvent: + flag);
                break;
        }


        return flag;
    }


然後運行,點擊InnerView  看log 

toucheventdemo E/OutView﹕ dispatchTouchEvent
toucheventdemo E/OutView﹕ onInterceptTouchEvent
toucheventdemo E/OutView﹕ onInterceptTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent:false
toucheventdemo E/InnerView﹕ dispatchTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent
toucheventdemo E/InnerView﹕ onTouchEvent:false
toucheventdemo E/InnerView﹕ dispatchTouchEvent:false
toucheventdemo E/MiddleView﹕ onTouchEvent
toucheventdemo E/MiddleView﹕ onTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent:false
toucheventdemo E/OutView﹕ onTouchEvent
toucheventdemo E/OutView﹕ onTouchEvent:false
toucheventdemo E/OutView﹕ dispatchTouchEvent:false

我們會發現,在所有的具體執行 dispatchTouchEvent 的方法一開始執行,都會先執行當前view 的onInterceptTouchEvent 結果,onInterceptTouchEvent 的默認返回值是false

現在我們修改一下MiddleView 的onInterceptTouchEvent 返回值為true 看代碼

    public boolean onInterceptTouchEvent(MotionEvent ev) {

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onInterceptTouchEvent);
                break;
        }

        boolean flag = super.onInterceptTouchEvent(ev);


        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onInterceptTouchEvent: + true);
                break;
        }


        return true;
    }

然後我們運行,看log

toucheventdemo E/OutView﹕ dispatchTouchEvent
toucheventdemo E/OutView﹕ onInterceptTouchEvent
toucheventdemo E/OutView﹕ onInterceptTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent:true
toucheventdemo E/MiddleView﹕ onTouchEvent
toucheventdemo E/MiddleView﹕ onTouchEvent:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent:false
toucheventdemo E/OutView﹕ onTouchEvent
toucheventdemo E/OutView﹕ onTouchEvent:false
toucheventdemo E/OutView﹕ dispatchTouchEvent:false

我們會發現,當修改了,MiddleView 的onInterceptTouchEvent 返回結果是true 之後,
本來應該向InnerView 做分發的那部分log 沒有了。然後直接執行了當前MiddleView 的 onTouchEvent 結果,然後執行完
onTouchEvent結果,之後,當前view 的dispatchTouchEvent 的結果也出來了,然後緊接著把這個有反饋給了OutView 外層view 的,並且執行的原理和我們前面只重寫 dispatchTouchEvent onTouchEvent 的原理一樣。

到這裡你也應該知道這個onInterceptTouchEvent 是干嘛的吧,他其實就是攔截事件是否要繼續往下傳遞,如果返回true ,那麼代表 事件被當前view 攔截,並且不向下傳遞,直接執行當前view 的 onTouchEvent 事件。

到這裡,我想應該清楚這幾個方法的含義了吧。

onInterceptTouchEvent : 攔截事件,判斷是否向下傳遞。 返回true 代表攔截,返回false 代表不攔截

dispatchTouchEvent : 事件分發,管理事件的分發,向子view 分發事件,並且,根據得到的子view的事件處理情況,來判斷是否響應當前view的 觸摸事件 返回true,代表當前view 的或者子view 已經處理事件,告訴父view, 不需要處理事件,返回false ,代表當前view 或者子view 也沒有處理事件,告訴父view,可以去處理事件了。

onTouchEvent : 事件的處理,在此方法內處理事件。 返回true ,代表當前view 處理了事件,false 代表當前view 沒有處理事件。

然後我們再看另外一個可能常用的方法,

public void requestDisallowInterceptTouchEvent(boolean disallowIntercept)

首先翻譯: 請求禁止攔截觸摸事件 ,字面理解,就是禁止吊我們的攔截,也就是禁止吊這個方法,onInterceptTouchEvent

那麼我們現在把所有的onInterceptTouchEvent dispatchTouchEvent onTouchEvent 都加上move 事件。

代碼如下,


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

        boolean flag = super.onInterceptTouchEvent(ev);


        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onInterceptTouchEvent ACTION_DOWN: + flag);
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, onInterceptTouchEvent ACTION_MOVE: + flag);
                break;
        }


        return flag;
    }


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

        boolean flag = super.dispatchTouchEvent(ev);

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, dispatchTouchEvent ACTION_DOWN: + flag);
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, dispatchTouchEvent ACTION_MOVE: + flag);
                break;
        }


        return flag;
    }


    @Override
    public boolean onTouchEvent(MotionEvent ev) {


        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onTouchEvent ACTION_DOWN);
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, onTouchEvent ACTION_MOVE );
                break;
        }

        boolean flag = super.onTouchEvent(ev);

        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onTouchEvent ACTION_DOWN: + flag);
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, onTouchEvent ACTION_MOVE: + flag);
                break;
        }


        return flag;


    }

並且給 MiddleView 的onInterceptTouchEvent 方法中加上 requestDisallowInterceptTouchEvent(true);
如下

 public boolean onInterceptTouchEvent(MotionEvent ev) {

        //加上此句
        requestDisallowInterceptTouchEvent(true);
        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;
        }

        boolean flag = super.onInterceptTouchEvent(ev);


        switch (ev.getAction()) {

            case MotionEvent.ACTION_DOWN:
                Log.e(TAG, onInterceptTouchEvent ACTION_DOWN: + flag);
                break;
            case MotionEvent.ACTION_MOVE:
                Log.e(TAG, onInterceptTouchEvent ACTION_MOVE: + flag);
                break;
        }


        return flag;
    }

那麼現在我們觸摸,然後看log ,可能log 比較多,但是希望大家慢慢仔細看。

 toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_DOWN
toucheventdemo E/OutView﹕ onInterceptTouchEvent ACTION_DOWN
toucheventdemo E/OutView﹕ onInterceptTouchEvent ACTION_DOWN:false
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_DOWN
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent ACTION_DOWN
toucheventdemo E/MiddleView﹕ onInterceptTouchEvent ACTION_DOWN:false
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_DOWN
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_DOWN
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_DOWN:true
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_DOWN:true
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_DOWN:true
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_DOWN:true

//從這裡開始move事件。
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_MOVE:true
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_MOVE
toucheventdemo E/InnerView﹕ onTouchEvent ACTION_MOVE:true
toucheventdemo E/InnerView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/MiddleView﹕ dispatchTouchEvent ACTION_MOVE:true
toucheventdemo E/OutView﹕ dispatchTouchEvent ACTION_MOVE:true

我們可以看處理啊,第一次是ACTION_DOWN 事件的時候一切正常,(InnerView 是繼承的 Button,所以onTouchEvent 返回的是true)

但是從ACTION_MOVE 你會發現,所有的onInterceptTouchEvent 事件全部沒有了,其實,也就這個方法也就是這個方法起的作用,

好了,所有的東西講完了,不知道你現在是否對事件傳遞有足夠的理解那?

 

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved