Android觸摸事件傳遞機制,這一篇就夠了


整個觸摸事件牽涉到的是,Activity,View,ViewGroup三者的傳遞機制。
這個觸摸事件就是從外層往內層一層層的傳遞。
整個傳遞機制,分為3個步驟:分發,攔截,和消費。

1. 觸摸事件的類型

事件類型是MotionEvent類:看下最新的sdk29的源碼,一堆的Action,我們常用的其實就3個,ACTION_DOWN,ACTION_MOVE,ACTION_UP。
Down和Up事件,是觸摸過程中一定會發生的事件。Move事件看具體的情況。

2.事件傳遞的順序

從外層往內層一層層的傳遞,外層是那個,內層是那個?
我們從android系統的角度看。事件首先傳給的肯定是Activity,因為Activity的UI布局,事物邏輯,作為底層來說,不知道,也並不關心。
所以只要把事件送到Activity層,就可以。
同樣的道理,事件先送到ViewGroup,然后在送到View。這樣從外層到內層的過程。
其實每一層都會對觸摸事件有響應,這個事件是有具體的業務邏輯決定的,所以我們需要一些操作來控制這個事件的過程。
比如點擊事件,假設點擊了某個按鈕,這樣它的父ViewGroup要不要處理,頁面要不要處理,從表面來看他們都被點擊了,從實際人們的經驗來看,
就是View被點擊了,不需要ViewGroup和Activtiy后面2個事件觸發。
Activity->ViewGroup->ViewGroup...->View 整個事件流程傳遞就是這樣。
View->ViewGroup...->ViewGroup->Activity 這個就是事件的消費過程。

3.事件傳遞的3個階段

所以android把觸摸事件傳遞分為3個步驟:分發,攔截,和消費。

3.1 分發

分發:事件分發對應着dispatchTouchEvent方法,在Android中,所有的觸摸事件都是這個方法來分發的。

public boolean dispatchTouchEvent(MotionEvent ev)

3.1.1 Activity分發

分發是觸摸事件首先到達。

  • 返回true,表示事件被消耗掉。不需要分發下去。消費了事件,后續的事件會繼續傳遞到這里。
  • 返回false,表示事件被廢棄掉。因為dispatchTouchEvent是總入口,所以就算是false,后續事件還會出現。
  • 返回值是super.dispatchTouchEvent,事件繼續傳遞下去,這個是整個事件傳遞的中樞,從源碼看,一切從這來開始,也從這里結束。
    如果view hierarchy都不處理這個touch事件,就由Activity的onTouch事件來處理。可見上面第二節的結論是正確的。最先接收,最后處理。
/**
     * Called to process touch screen events.  You can override this to
     * intercept all touch screen events before they are dispatched to the
     * window.  Be sure to call this implementation for touch screen events
     * that should be handled normally.
     *
     * @param ev The touch screen event.
     *
     * @return boolean Return true if this event was consumed.
     */
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            onUserInteraction();
        }
        if (getWindow().superDispatchTouchEvent(ev)) {
            return true;
        }
        return onTouchEvent(ev);
    }

3.1.2VIewGroup分發

  • 返回true,表示事件被消耗掉。不需要分發下去。消費了事件,后續的事件會繼續傳遞到這里。
  • 返回false,表示事件被廢棄掉。后續的其他事件不會被傳遞到這里。
  • 返回值是super.dispatchTouchEvent,事件繼續傳遞下去,這個是整個事件傳遞的中樞,從源碼看,ViewGroup的分發流程分以下幾步
    有攔截,攔截了touch事件,就走ViewGroup自己的onTouchEvent。
    沒有攔截,就找到觸摸點的位置,上面是否有View,有就調view.dispatchTouchEvent,如果沒有,就直接交給ViewGroup處理。
    這里還有一點,如果action_down已經處理了,后面action_up 和 action_move都使用相同的處理方式。

3.1.3 View的分發

View的分發,也許會覺得奇怪,分發給誰?
從源碼看,2個途徑,onTouch listener,或者onTouchEvent。前者優先級必后者高。

  • 返回true,表示事件被消耗掉。不需要分發下去。消費了事件,后續的事件會繼續傳遞到這里。並且這里會認為事件被View消費掉了,所以上層的VIewGroup不會收到要處理onTouchEvent的事件。
  • 返回false,表示事件被廢棄掉。后續的其他事件不會被傳遞到這里。而且可以理解為,我這個View不想處理這個事件,所以ViewGroup會觸發onTouchEvent事件。
  • 返回值是super.dispatchTouchEvent
            ListenerInfo li = mListenerInfo;
            if (li != null && li.mOnTouchListener != null
                    && (mViewFlags & ENABLED_MASK) == ENABLED
                    && li.mOnTouchListener.onTouch(this, event)) {
                result = true;
            }

            if (!result && onTouchEvent(event)) {
                result = true;
            }

onTouch是否處理,return true,就表面被第三方處理了,不需要onTouchEvent來處理。不然就是onTouchEvent處理。

3.2 攔截

事件攔截對應onInterceptTouchEvent。事件是從外向內傳遞的,當事件在分發的過程中,要不要攔截,

public boolean onInterceptTouchEvent(MotionEvent ev)

有些情況下需要子View來響應,有些情況需要當前ViewGroup來處理。這個方法Activity沒有,只有ViewGroup才有。
這個簡單來說就是,是否需要分發事件給到子View,true就是不分發。

3.3 消費

其實dispatchTouchEvent也有消費的概念在里面。
如果說分發的流程,是從外層到內層,
消費的流程正好相反,從內層到外層。

public boolean onTouchEvent(MotionEvent event) 
  • 返回true,消費事件,不在向外層傳遞
    -返回false,不消費事件,向外層傳遞
    -返回spuer.onTouchEvent,這里會判斷是否是click事件。只有這個寫法才會觸發click事件。

4. 流程圖

5.總結

流程很復雜,每個部分返回不同的true/fase/super都會產生不同的流程結果。
事件分發流程:
從外層到內層分發,
從內層到外層消費。

dispatchTouchEvent 和onTouchEvent,返回true,代表由自己處理這個事情。阻斷后面的流程。
dispatchTouchEvent 和onTouchEvent,返回false,表示不由自己來處理這個事件,流程交由上一層級來處理。
dispatchTouchEvent 和onTouchEvent,返回spuer,就是上面的流程。

更多內容:demanmath
公共號:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM