今天總結的一個知識點是Andorid中View事件傳遞機制,也是核心知識點,相信很多開發者在面對這個問題時候會覺得困惑,另外,View的另外一個難題滑動沖突,比如在ScrollView中嵌套ListView,都是上下滑動,這該如何解決呢,它解決的依據就是View事件的傳遞機制,是以開發者需要對View的事件傳遞機制有較深入的了解。
Activity、View、ViewGroup三者關系
觸摸事件類型
事件傳遞三個階段
View事件傳遞機制
ViewGroup事件傳遞機制
小結
我們都知道Android中看到的頁面很多是Activity元件,然後在Activity中嵌套控件,比如TextView、RelativeLayout布局等,其實這些控件的基類都是View這個抽象類,而ViewGroup也是View的子類,差別在于ViewGroup是可以當做其他子類的容器,一張關系圖如下:

簡單一句話,這些View控件的載體是Activity,Activity通過從DecorView開始進行繪制。
<code>ACTION_DOWN</code>:使用者手指按下操作,往往也代表着一次觸摸事件的開始。
<code>ACTION_MOVE</code>:使用者手指在螢幕上移動,一般情況下的輕微移動都會觸發一系列的移動事件。
<code>ACTION_POINTER_DOWN</code>:額外的手指按下操作。
<code>ACTION_POINTER_UP</code>:額外的手指的離開操作
<code>ACTION_UP</code>:使用者手指離開螢幕的操作,一次擡起操作标志着一次觸摸事件的結束。
在一次螢幕觸摸操作中,<code>ACTION_DOWN</code>和<code>ACTION_UP</code>是必需的,<code>ACTION_MOVE</code>則是看情況而定,如果隻是點選,那麼檢測到隻有按下和擡起操作。
分發(Dispatch):事件的分發對應着dispatchTouchEvent方法,在Andorid系統中,所有的觸摸事件都是通過這個方法來分發的。
這個方法中,可以決定直接消費這個事件或者将事件繼續分發給子視圖處理。
攔截(Intercept):事件攔截對應着onInterceptTouchEvent方法,這個方法隻有在ViewGroup及其子類中才存在,在View和Activity中是不存在的。
這個方法用來判斷是否攔截某個事件,如果攔截了某個事件,那麼在同一序列事件當中,那麼這個方法不會被再次調用。
消費(Consume):事件消費對應着onTouchEvent方法。
用來處理點選事件,傳回結果表示是否消耗目前事件,如果不消耗,則在同一事件序列中,目前View無法再接收到事件
在Android系統中,擁有事件傳遞處理能力的有三種:
Activity:擁有dispatchTouchEvent、onTouchEvent兩個方法。
ViewGroup:擁有dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三個方法。
View:擁有dispatchTouchEvent、onTouchEvent兩個方法。
這裡說的View指的是除了ViewGroup之外的View控件,比如TextView、Button、CheckBox等,View控件本身就是最小的機關,不能作為其他View的容器,View擁有dispatchTouchEvent、onTouchEvent兩個方法,是以這裡就定義了一個繼承TextView的類MyTextView,通過代碼檢視日志,看流程如何走。
同時定義一個MainActivity類用來展示MyTextView,在這個Activity中,我們為MyTextView設定了點選onClick和onTouch監聽,友善跟蹤了解事件傳遞的流程。
檢視結果:
從中可以看到,事件是從down-move-up這樣順序執行,onTouch方法優先于onClick方法調用,如果都是以super方法傳遞的話,最後的結果是在MyTextView的onTouchEvent方法内被消費的,如果不消費的話,則會把事件傳回到它的父級去消費,如果父級也沒消費,那麼最終會傳回到Activity中處理。
ViewGroup作為View控件的容器存在,ViewGroup擁有dispatchTouchEvent、onInterceptTouchEvent、onTouchEvent三個方法。同樣,我們自定義一個ViewGroup,繼承自RelativeLayout,實作一個MyRelativeLayout。
從中可以看到觸摸事件的傳遞順序也是從Activity到ViewGroup,再由ViewGroup遞歸傳遞給它的子View。ViewGroup通過onInterceptTouchEvent方法對事件進行攔截,如果該方法傳回true,則事件不會繼續傳遞給子View,如果傳回false或者super.onInterceptTouchEvent,則事件會繼續傳遞給子View。在子View中對事件進行消費後,ViewGroup将不接收到任何事件。
在Android系統事件中,View和ViewGroup的僞代碼如下:
用三張圖來表示Android中觸摸機制的流程。
1,View内觸摸事件不消費
2,View内觸摸事件消費
3,ViewGroup攔截觸摸事件
一些總結:
同一個事件序列是指從手指接觸螢幕的那一刻起,到手指離開螢幕的那一刻結束。一般是以down事件開始,中間含有數量不定的move事件,最終以up事件結束。
正常情況下,一個事件序列隻能被一個View攔截且消耗。
某個View一旦決定攔截,那麼這個事件序列就隻能由它來處理,那麼同一事件序列中的其他事件都不會再交給它來處理,并且事件将重新交給它的父元素去處理,即父元素的onTouchEvent會被調用。
如果View不消耗除ACTION_DOWN以外的其他事件,那麼這個點選事件就會消失,此時父元素的onTouchEvent并不會被調用,最終會交給Activity處理。
ViewGroup預設不攔截任何事件。
View中沒有onInterceptTouchEvent方法。
View的onTouchEvent預設都會被消耗,除非它是不可點選的。
事件傳遞過程是由外向内的,即事件先是傳遞給父元素,然後再由父元素分發給子View。
參考位址:
1,https://www.youtube.com/watch?v=EZAoJU-nUyI
源于對掌握的Android開發基礎點進行整理,羅列下已經總結的文章,從中可以看到技術積累的過程。
1,Android系統簡介
2,ProGuard代碼混淆
3,講講Handler+Looper+MessageQueue關系
4,Android圖檔加載庫了解
5,談談Android運作時權限了解
6,EventBus初了解
7,Android 常見工具類
8,對于Fragment的一些了解
9,Android 四大元件之 " Activity "
10,Android 四大元件之" Service "
11,Android 四大元件之“ BroadcastReceiver "
12,Android 四大元件之" ContentProvider "
13,講講 Android 事件攔截機制
14,Android 動畫的了解
15,Android 生命周期和啟動模式
16,Android IPC 機制
17,View 的事件體系
18,View 的工作原理
19,了解 Window 和 WindowManager
20,Activity 啟動過程分析
21,Service 啟動過程分析
22,Android 性能優化
23,Android 消息機制
24,Android Bitmap相關
25,Android 線程和線程池
26,Android 中的 Drawable 和動畫
27,RecylerView 中的裝飾者模式
28,Android 觸摸事件機制
29,Android 事件機制應用
30,Cordova 架構的一些了解
31,有關 Android 插件化思考
32,開發人員必備技能——單元測試