Android事件分發機制
在安卓四大元件中( Activity
、 Service
、 BroadCast
、 ContentProvider
),最常用的當是 Activity
。因為 Activity
負責提供直觀的頁面並響應使用者操作。在 Activity
的佈局檔案中,通過最外層的 ViewGroup
(佈局)一層層巢狀、佈局直至 View
(控制元件),組成了豐富多彩的使用者頁面。如QQ、微信等等。在開發這些頁面過程中,難免會遇到一些事件衝突(說人話就是:你想點選的某個佈局或控制元件,發現響應的是另一個佈局或控制元件)的問題。呵呵,怎麼辦呢?由此引入我們接下來討論的問題。
事件分發闡述
手機在響應使用者的點選操作時,從 Activity
入口,遵循著一定的規則將對應的事件交由指定的 ViewGroup
或者 View
去響應(消費這個事件)。只有找出了各中規則,日後再次處理這類事件問題時,必定會得心應手。從程式的角度來看, Android
提供三個方法處理事件問題 dispatchTouchEvent
( 分發點選事件 )、 onInterceptTouchEvent
( 攔截點選事件 ), onTouchEvent
( 處理點選事件 )。每個方法返回 true
或 false
表示是否處理它對應的職責。比如說,如果我攔截了事件,就表示自身要處理該事件,不讓別的控制元件再能接收到事件訊號。除了返回 true
或 false
之外,還能通過呼叫父類的方法執行父類的邏輯。簡而言之, Activity
、 ViewGroup
、 View
三個類處理事件,相關的方法依次是 dispatchTouchEvent
、 onInterceptTouchEvent
、 onTouchEvent
以及每個方法的返回可以是 true
、 false
和 super
。其中注意: Activity
和 View
沒有攔截事件方法。
驗證
編寫一個測試demo,佈局檔案中用 LinearLayout
巢狀 RelativeLayout
,最後在 RelativeLayout
中包含一個 Button
按鈕。依次在每個事件處理方法中,輸出log日誌。點選按鈕測試,通過日誌資訊分析事件分發流程。下圖是佈局效果。

為方便描述,定製命名規則: Activity
簡寫 A , LinearLayout
簡寫 L , RelativeLayout
簡寫 R , Button
簡寫 B , dispatchTouchEvent
簡寫 D , onInterceptTouchEvent
簡寫 I , onTouchEvent
簡寫 T ,返回 true
簡寫 T ,返回 false
簡寫 F ,返回 super
簡寫 S 。如: ADS 表示 Activity
中 dispatchTouchEvent
方法 return super
。
測試方案一
ADS/ATS、LDS/LIS/LTS、RDS/RIS/RTS、BDS/BTS 複製程式碼
日誌說明
04-01 12:44:44.402: D/Activity(1234): dispatchTouchEvent ACTION_DOWN 04-01 12:44:44.402: D/MyLinearLayout(1234): dispatchTouchEvent ACTION_DOWN 04-01 12:44:44.402: D/MyLinearLayout(1234): onInterceptTouchEvent ACTION_DOWN 04-01 12:44:44.402: D/MyRelativeLayout(1234): dispatchTouchEvent ACTION_DOWN 04-01 12:44:44.402: D/MyRelativeLayout(1234): onInterceptTouchEvent ACTION_DOWN 04-01 12:44:44.402: D/MyButton(1234): dispatchTouchEvent ACTION_DOWN 04-01 12:44:44.402: D/MyButton(1234): onTouchEvent ACTION_DOWN 04-01 12:44:44.506: D/Activity(1234): dispatchTouchEvent ACTION_UP 04-01 12:44:44.506: D/MyLinearLayout(1234): dispatchTouchEvent ACTION_UP 04-01 12:44:44.506: D/MyLinearLayout(1234): onInterceptTouchEvent ACTION_UP 04-01 12:44:44.506: D/MyRelativeLayout(1234): dispatchTouchEvent ACTION_UP 04-01 12:44:44.506: D/MyRelativeLayout(1234): onInterceptTouchEvent ACTION_UP 04-01 12:44:44.506: D/MyButton(1234): dispatchTouchEvent ACTION_UP 04-01 12:44:44.506: D/MyButton(1234): onTouchEvent ACTION_UP 04-01 12:44:44.506: D/Button(1234): 點選 複製程式碼
整理出如下示意圖:三色箭頭實線,表示對應方法返回一個值會跳轉到下一個方法。其中Button 返回true和返回super都會消費事件,只是返回true就不在就不再響應button的點選事件。

測試方案二
ADS/ATS、LDS/LIS/LTS、RDS/RIS/RTS、BDS/BTF 複製程式碼
日誌說明
04-01 14:00:54.970: D/Activity(1387): dispatchTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/MyLinearLayout(1387): dispatchTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/MyLinearLayout(1387): onInterceptTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/MyRelativeLayout(1387): dispatchTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/MyRelativeLayout(1387): onInterceptTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/MyButton(1387): dispatchTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/MyButton(1387): onTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/MyRelativeLayout(1387): onTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/MyLinearLayout(1387): onTouchEvent ACTION_DOWN 04-01 14:00:54.970: D/Activity(1387): onTouchEvent ACTION_DOWN 04-01 14:00:55.066: D/Activity(1387): dispatchTouchEvent ACTION_UP 04-01 14:00:55.066: D/Activity(1387): onTouchEvent ACTION_UP 複製程式碼

說明:Button 的onTouchEvent return false時,點選事件會返回到上一層檢視。如果一直都沒有控制元件處理這個事件就會交給Activitiy 的onTouchEvent消費。當再傳入事件時,不會再按照之前的流程,每個空間均處理一次,而是直接給 Activity 的onTouchEvent消費。就是虛線所指。
測試方案三
ADS/ATS、LDS/LIS/LTS、RDS/RIS/RTS、BDT/BTF 複製程式碼
日誌說明
04-01 14:12:47.918: D/Activity(1444): dispatchTouchEvent ACTION_DOWN 04-01 14:12:47.918: D/MyLinearLayout(1444): dispatchTouchEvent ACTION_DOWN 04-01 14:12:47.918: D/MyLinearLayout(1444): onInterceptTouchEvent ACTION_DOWN 04-01 14:12:47.918: D/MyRelativeLayout(1444): dispatchTouchEvent ACTION_DOWN 04-01 14:12:47.918: D/MyRelativeLayout(1444): onInterceptTouchEvent ACTION_DOWN 04-01 14:12:47.918: D/MyButton(1444): dispatchTouchEvent ACTION_DOWN 04-01 14:12:48.030: D/Activity(1444): dispatchTouchEvent ACTION_UP 04-01 14:12:48.030: D/MyLinearLayout(1444): dispatchTouchEvent ACTION_UP 04-01 14:12:48.030: D/MyLinearLayout(1444): onInterceptTouchEvent ACTION_UP 04-01 14:12:48.030: D/MyRelativeLayout(1444): dispatchTouchEvent ACTION_UP 04-01 14:12:48.030: D/MyRelativeLayout(1444): onInterceptTouchEvent ACTION_UP 04-01 14:12:48.030: D/MyButton(1444): dispatchTouchEvent ACTION_UP 複製程式碼

說明:Button dispatchTouchEvent方法返回true,表示自身需要消費該事件。且不傳遞給onTouchEvent方法。
測試方案四
ADS/ATS、LDS/LIS/LTS、RDS/RIS/RTS、BDF/BTF 複製程式碼
日誌說明
04-01 14:19:40.014: D/Activity(1502): dispatchTouchEvent ACTION_DOWN 04-01 14:19:40.014: D/MyLinearLayout(1502): dispatchTouchEvent ACTION_DOWN 04-01 14:19:40.014: D/MyLinearLayout(1502): onInterceptTouchEvent ACTION_DOWN 04-01 14:19:40.014: D/MyRelativeLayout(1502): dispatchTouchEvent ACTION_DOWN 04-01 14:19:40.014: D/MyRelativeLayout(1502): onInterceptTouchEvent ACTION_DOWN 04-01 14:19:40.014: D/MyButton(1502): dispatchTouchEvent ACTION_DOWN 04-01 14:19:40.014: D/MyRelativeLayout(1502): onTouchEvent ACTION_DOWN 04-01 14:19:40.014: D/MyLinearLayout(1502): onTouchEvent ACTION_DOWN 04-01 14:19:40.014: D/Activity(1502): onTouchEvent ACTION_DOWN 04-01 14:19:40.022: D/Activity(1502): dispatchTouchEvent ACTION_UP 04-01 14:19:40.022: D/Activity(1502): onTouchEvent ACTION_UP 複製程式碼

說明:Button dispatchTouchEvent方法返回false,事件傳遞給父控制元件onTouchEvent方法。
測試方案五
ADS/ATS、LDS/LIS/LTS、RDS/RIS/RTT、BDF/BTF 複製程式碼
日誌說明
04-01 14:26:03.306: D/Activity(1561): dispatchTouchEvent ACTION_DOWN 04-01 14:26:03.306: D/MyLinearLayout(1561): dispatchTouchEvent ACTION_DOWN 04-01 14:26:03.306: D/MyLinearLayout(1561): onInterceptTouchEvent ACTION_DOWN 04-01 14:26:03.306: D/MyRelativeLayout(1561): dispatchTouchEvent ACTION_DOWN 04-01 14:26:03.306: D/MyRelativeLayout(1561): onInterceptTouchEvent ACTION_DOWN 04-01 14:26:03.306: D/MyButton(1561): dispatchTouchEvent ACTION_DOWN 04-01 14:26:03.306: D/MyRelativeLayout(1561): onTouchEvent ACTION_DOWN 04-01 14:26:03.366: D/Activity(1561): dispatchTouchEvent ACTION_UP 04-01 14:26:03.366: D/MyLinearLayout(1561): dispatchTouchEvent ACTION_UP 04-01 14:26:03.366: D/MyLinearLayout(1561): onInterceptTouchEvent ACTION_UP 04-01 14:26:03.366: D/MyRelativeLayout(1561): dispatchTouchEvent ACTION_UP 04-01 14:26:03.366: D/MyRelativeLayout(1561): onTouchEvent ACTION_UP 複製程式碼

說明:RelativeLayout 的onTouchEvent方法return true 消費了事件,下次事件生成時,就不傳遞給Button,直接交由RelativeLayout 的onTouchEvent方法處理。
測試方案六
ADS/ATS、LDS/LIS/LTS、RDS/RIS/RTF、BDF/BTF 複製程式碼
日誌說明
04-01 14:39:20.794: D/Activity(1644): dispatchTouchEvent ACTION_DOWN 04-01 14:39:20.798: D/MyLinearLayout(1644): dispatchTouchEvent ACTION_DOWN 04-01 14:39:20.798: D/MyLinearLayout(1644): onInterceptTouchEvent ACTION_DOWN 04-01 14:39:20.798: D/MyRelativeLayout(1644): dispatchTouchEvent ACTION_DOWN 04-01 14:39:20.798: D/MyRelativeLayout(1644): onInterceptTouchEvent ACTION_DOWN 04-01 14:39:20.798: D/MyButton(1644): dispatchTouchEvent ACTION_DOWN 04-01 14:39:20.798: D/MyRelativeLayout(1644): onTouchEvent ACTION_DOWN 04-01 14:39:20.798: D/MyLinearLayout(1644): onTouchEvent ACTION_DOWN 04-01 14:39:20.798: D/Activity(1644): onTouchEvent ACTION_DOWN 04-01 14:39:20.874: D/Activity(1644): dispatchTouchEvent ACTION_UP 04-01 14:39:20.874: D/Activity(1644): onTouchEvent ACTION_UP 複製程式碼

說明:RelativeLayout return false,就會將事件傳遞到上一層控制元件消費。其中,RelativeLayout onInterceptTouchEvent return false 表示事件也會分發到Button處理。
測試方案七
ADS/ATS、LDS/LIS/LTS、RDS/RIT/RTF、BDF/BTF 複製程式碼
日誌說明
04-01 15:02:41.594: D/Activity(1739): dispatchTouchEvent ACTION_DOWN 04-01 15:02:41.594: D/MyLinearLayout(1739): dispatchTouchEvent ACTION_DOWN 04-01 15:02:41.594: D/MyLinearLayout(1739): onInterceptTouchEvent ACTION_DOWN 04-01 15:02:41.594: D/MyRelativeLayout(1739): dispatchTouchEvent ACTION_DOWN 04-01 15:02:41.594: D/MyRelativeLayout(1739): onInterceptTouchEvent ACTION_DOWN 04-01 15:02:41.594: D/MyRelativeLayout(1739): onTouchEvent ACTION_DOWN 04-01 15:02:41.594: D/MyLinearLayout(1739): onTouchEvent ACTION_DOWN 04-01 15:02:41.594: D/Activity(1739): onTouchEvent ACTION_DOWN 04-01 15:02:41.714: D/Activity(1739): dispatchTouchEvent ACTION_UP 04-01 15:02:41.714: D/Activity(1739): onTouchEvent ACTION_UP 複製程式碼

說明:RelativeLayout onInterceptTouchEvent 返回true,表示RelativeLayout 攔截事件資訊,Button不會響應事件,事件交由RelativeLayout onTouchEvent處理。
測試方案八
ADS/ATS、LDS/LIS/LTS、RDT/RIT/RTF、BDF/BTF 複製程式碼
日誌說明
04-01 15:27:17.002: D/Activity(1907): dispatchTouchEvent ACTION_DOWN 04-01 15:27:17.002: D/MyLinearLayout(1907): dispatchTouchEvent ACTION_DOWN 04-01 15:27:17.002: D/MyLinearLayout(1907): onInterceptTouchEvent ACTION_DOWN 04-01 15:27:17.002: D/MyRelativeLayout(1907): dispatchTouchEvent ACTION_DOWN 04-01 15:27:17.058: D/Activity(1907): dispatchTouchEvent ACTION_UP 04-01 15:27:17.058: D/MyLinearLayout(1907): dispatchTouchEvent ACTION_UP 04-01 15:27:17.058: D/MyLinearLayout(1907): onInterceptTouchEvent ACTION_UP 04-01 15:27:17.058: D/MyRelativeLayout(1907): dispatchTouchEvent ACTION_UP 複製程式碼

說明:RelativeLayout dispatchTouchEvent return true 表示自己消費事件,不再進行傳遞
測試方案九
ADS/ATS、LDS/LIS/LTS、RDF/RIT/RTF、BDF/BTF 複製程式碼
日誌說明
04-01 15:32:12.138: D/Activity(1965): dispatchTouchEvent ACTION_DOWN 04-01 15:32:12.138: D/MyLinearLayout(1965): dispatchTouchEvent ACTION_DOWN 04-01 15:32:12.138: D/MyLinearLayout(1965): onInterceptTouchEvent ACTION_DOWN 04-01 15:32:12.138: D/MyRelativeLayout(1965): dispatchTouchEvent ACTION_DOWN 04-01 15:32:12.138: D/MyLinearLayout(1965): onTouchEvent ACTION_DOWN 04-01 15:32:12.138: D/Activity(1965): onTouchEvent ACTION_DOWN 04-01 15:32:12.250: D/Activity(1965): dispatchTouchEvent ACTION_UP 04-01 15:32:12.250: D/Activity(1965): onTouchEvent ACTION_UP 複製程式碼

說明:RelativeLayout dispatchTouchEvent return false 表示不處理事件,交給上層頁面處理。
測試方案十
ADT/ATS、LDS/LIS/LTS、RDF/RIT/RTF、BDF/BTF 複製程式碼
日誌說明
04-01 15:43:24.558: D/Activity(2023): dispatchTouchEvent ACTION_DOWN 04-01 15:43:24.686: D/Activity(2023): dispatchTouchEvent ACTION_UP 複製程式碼

說明:同ViewGroup和View的 dispatchTouchEvent 方法一樣,return true 表示自己消費事件。不同的是,Activity 的dispatchTouchEvent方法return false 也表示自己消費事件。
測試方案十一
ADS/ATS、LDS/LIS/LTS、RDF/RIT/RTF、BDF/BTF 複製程式碼
日誌說明
04-01 15:56:51.486: D/Activity(2149): dispatchTouchEvent ACTION_DOWN 04-01 15:56:51.486: D/MyLinearLayout(2149): dispatchTouchEvent ACTION_DOWN 04-01 15:56:51.486: D/MyLinearLayout(2149): onInterceptTouchEvent ACTION_DOWN 04-01 15:56:51.486: D/MyRelativeLayout(2149): dispatchTouchEvent ACTION_DOWN 04-01 15:56:51.486: D/MyLinearLayout(2149): onTouchEvent ACTION_DOWN 04-01 15:56:51.486: D/Activity(2149): onTouchEvent ACTION_DOWN 04-01 15:56:51.490: D/Activity(2149): dispatchTouchEvent ACTION_UP 04-01 15:56:51.490: D/Activity(2149): onTouchEvent ACTION_UP 複製程式碼

說明:其實,最終只要傳遞到Activity的onTouchEvent 方法,都會交由它處理事件。
參照之前理解的思路,將整個示意圖完善如下:
