1. 程式人生 > >進階必備-Android事件分發機制

進階必備-Android事件分發機制

一、簡介

或許你會問,“為什麼我一定要知道View的事件分發機制?”。因為我們在實際開發的過程中,經常會遇到多層的View互相巢狀以後,對某一個View進行滑動的時候,特別不靈敏,甚至於沒法滑動。這種滑動衝突的解決需要我們清楚的掌握View的事件分發機制。那下面我們詳細的講解下View的整個事件機制。
Android將View的事件封裝到MotionEvent這個類中,這也是監聽touch事件中回撥給我們的引數public boolean onTouchEvent(MotionEvent event) 。通常事件我們主要關心下面幾種型別:

  • MotionEvent.ACTION_DOWN


    當我們手指按下螢幕的第一個事件便是ACTION_DOWN了,也就是意味著事件的開始。

  • MotionEvent.ACTION_MOVE
    當我們手指按下屏幕後,在螢幕上滑動的過程,此事件就會不斷的觸發。

  • MotionEvent.ACTION_UP
    此事件在我們手指從螢幕擡起的時候會觸發。

  • MotionEvent.ACTION_CANCEL
    這個事件說起來稍微複雜一點,舉個栗子:當我們的外層View將事件傳遞給內層View去處理時,外層View的攔截方法一般會返回false,但是當某個條件觸發後,外層View想自己處理接下來的事件,就攔截了事件分發,此時內層View就會收到ACTION_CANCEL的事件。

  • MotionEvent.ACTION_OUTSIDE
    這個事件我們不常用到,考慮這種場景。我們又一個Diallog彈出,當我們按Dialog以外的螢幕將Dialog消失掉。這個時候可以考慮監聽這個事件,要想使用這個事件我們必須對當前的Window設定一個Flag:FLAG_WATCH_OUTSIDE_TOUCH

下面我們介紹和事件分發相關的幾個方法:

  • dispatchTouchEvent(MotionEvent event)
    這個方法是用來處理向下分發事件邏輯的,我們通過觀察ViewGrope原始碼中的程式碼知道,這個方法細節較多,檢出我們比較關心的邏輯就是這個方法會先判斷子View是否有呼叫disallowIntercept父View去攔截事件,如果沒有,父View自己會呼叫onInterceptTouchEvent判斷自己是否有攔截,如果攔截事件,將呼叫父View自己的onTouchEvent方法去處理事件,如果沒有攔截事件,事件將繼續分發到子View中處理。

  • onInterceptTouchEvent(MotionEvent event)
    用來申明是否攔截事件繼續向下分發,如果返回true,事件將不會繼續向下分發,而是交由自己的onTouchEvent方法處理。

  • onTouchEvent(MotionEvent event)
    顯然,這個就是事件處理的方法了。

  • onTouch(MotionEvent event)
    這個方法是在我們對某一個setOnTouchListener時回撥,也就是在傳遞事件的時候,在交給View本身的onTouchEvent處理之前判斷是否有監聽的TouchListener,如果有優先呼叫TouchListener的onTouch方法處理。

二、詳細分析View的分發事件

我們都知道,Android的View是樹形結構的,所以當一個事件來臨的時候一般是從根部分發下來的。為了方便我們接下來的理解,我們構建一個這樣的例子:

假設我們有這樣一個頁面,最外層是一個ViewGroup A,裡面巢狀著一個ViewGroup B,B裡面有一個ViewGroup C。

在這裡插入圖片描述

情景1:

假設我們對事件不做任何攔截,也不做任何處理。當我們點選View C,這個時候我們看到的Log顯示呼叫順序為:

A -> dispatchTouchEvent
A -> onInterceptTouchEvent
B -> dispatchTouchEvent
B -> onInterceptTouchEvent
C -> dispatchTouchEvent
C -> onInterceptTouchEvent
C -> onTouchEvent ACTION_DOWN
B -> onTouchEvent ACTION_DOWN
A -> onTouchEvent ACTION_DOWN

由於沒有任何View處理事件,最終會回撥到Activity的onTouchEvent中去處理。從這個情景中我們可以知道,事件向下傳遞的過程以及處理事件的向上傳遞的過程。

情景2:

假設我們在View BonTouchEvent中返回true,再次點選事件並滑動,我們得到的Log如下:

A -> dispatchTouchEvent
A -> onInterceptTouchEvent
B -> dispatchTouchEvent
B -> onInterceptTouchEvent
C -> dispatchTouchEvent
C -> onInterceptTouchEvent
C -> onTouchEvent ACTION_DOWN
B -> onTouchEvent ACTION_DOWN

A -> dispatchTouchEvent
A -> onInterceptTouchEvent
B -> dispatchTouchEvent
B -> onTouchEvent ACTION_MOVE

A -> dispatchTouchEvent
A -> onInterceptTouchEvent
B -> dispatchTouchEvent
B -> onTouchEvent ACTION_UP  

我們發現,除了ACTION_DOWN事件會下發到C,後續的事件不會再下發這是因為,當我們發現某一層View的onTouchEvent返回true以後,會有一個標誌位表示後續的事件都由此View處理,後續事件不再下發到子View,直到ACTION UP事件後將標誌位重置。

情景3:

假設我們在View B的onInterceptTouchEvent中返回true,再次點選C會怎麼樣呢?我們得到如下的Log記錄:

A -> dispatchTouchEvent
A -> onInterceptTouchEvent
B -> dispatchTouchEvent
B -> onInterceptTouchEvent
B -> onTouchEvent ACTION_DOWN
A -> onTouchEvent ACTION_DOWN

相比較於情景2,ACTION_DOWN事件不會下發到C,由於沒有View表示能處理,所以後續的事件均被取消。

三、總結

通過我們實際執行和分析原始碼發現,我們ViewGroup事件的分發流程如下所示:
在這裡插入圖片描述
對著上圖大家不妨嘗試分析下:

  • 如果B的onInterceptTouchEvent中返回true並且onTouchEvent中返回true,那麼Log又將是怎樣的呢?

可以關注微信公眾號:南京Android部落,回覆“666”獲取答案哦~