事件都是從用戶按下(ACTION_DOWN)的那一刻產生的,三個非常重要的與事件相關的方法:
- dispatchTouchEvent()
- onTouchEvent()
- onInterceptTouchEvent()
Activity 的事件分發機制
從單詞含義已經很明顯的知道,dispatchTouchEvent()
是負責事件分發的。當點擊事件產生後,事件首先會傳遞給當前的 Activity,這會調用 Activity 的 dispatchTouchEvent()
方法,看看源碼中是怎麼處理的。
由於我們一般產生點擊事件都是 MotionEvent.ACTION_DOWN
,所以一般都會調用到 onUserInteraction()
這個方法。
很遺憾,這個方法實現是空的,不過可以從註釋和其他途徑可以瞭解到,該方法主要的作用是實現屏保功能,並且當此 Activity 在棧頂的時候,觸屏點擊 Home、Back等都會觸發這個方法。
再來看看第二個 if 語句,getWindow().superDispatchTouchEvent()
,getWindow()
明顯是獲取 Window
,由於 Window
是一個抽象類,所以我們能拿到其子類 PhoneWindow
,直接看看 PhoneWindows.superDispatchTouchEvent()
到底做了什麼操作。
直接調用了 DecorView
的 superDispatchTrackballEvent()
方法。DecorView
繼承於 FrameLayout
,而 FrameLayout
作爲 ViewGroup
的子類,所以直接調用了 ViewGroup
的 dispatchTouchEvent()
。
ViewGroup 的事件分發機制
查看 ViewGroup
的 dispatchTouchEvent()
:
注意其中紅框裏面的代碼,看註釋也能知道,定義了一個 boolean 值變量 intercept
來表示是否要攔截事件。
其中採用到了 onInterceptTouchEvent(ev)
對 intercept
進行賦值。大多數情況下,onInterceptTouchEvent()
返回值爲 false,但我們完全可以通過重寫 onInterceptTouchEvent(ev)
來改變它的返回值,繼續往下看後面對這個 intercept
做了什麼處理。
暫時忽略 判斷的 canceled
,該值同樣大多數時候都返回 false,所以當我們沒有重寫 onInterceptTouchEvent()
並使它的返回值爲 true 時,一般情況下都是可以進入到該方法的。
繼續閱讀源碼可以發現,裏面做了一個 for 循環,通過倒序遍歷 ViewGroup
下面的所有子 View,然後一個一個判斷點擊位置是否是該子 View 的佈局區域,當然還有一些其他的,由於篇幅原因,這裏就不細講了。
View 的事件分發機制
ViewGroup
說到底還是一個 View,所以我們不得不繼續看看 View 的 dispatchTouchEvent()
。
-
(mViewFlags & ENABLED_MASK) == ENABLED
判斷當前點擊的控件是否爲 enable,但由於基本 View 都是 enable 的,所以這個條件基本都返回 true。 -
mOnTouchListener.onTouch(this, event)
即我們調用setOnTouchListener()
時必須覆蓋的方法onTouch()
的返回值。
從上述的分析,終於知道「onTouch()
方法優先級高於 onTouchEvent(event)
方法」是怎麼來的了!!
再看 onTouchEvent()
從上面的代碼可以明顯地看到,只要 View 的 CLICKABLE 和 LONG_CLICKABLE 有一個爲 true,那麼 onTouchEvent()
就會返回 true 消耗這個事件。CLICKABLE 和 LONG_CLICKABLE 代表 View 可以被點擊和長按點擊,我們通常都會採用 setOnClickListener()
和 setOnLongClickListener()
做設置。接着在 ACTION_UP 事件中會調用 performClick()
方法。
從截圖中可以看到,如果 mOnClickListener
不爲空,那麼它的 onClick()
方法就會調用。
總結
- Activity 的事件分發示意圖
- ViewGroup 事件分發示意圖
-
View 的事件分發示意圖
- 事件分發工作流程總結