一、簡介
或許你會問,“爲什麼我一定要知道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 B的onTouchEvent中返回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又將是怎樣的呢?
可以關注下方公衆號,回覆“666”獲取答案哦~