Android事件分發機制、滑動衝突解決
這裡所說的事件,是MotrionEvent。
所謂點選事件的時間分發,其實就是對MotionEvent事件的分發過程,即當一個MotionEvent產生以後,系統需要把這個事件傳遞給一個具體的View,而這個傳遞過程就是分發過程。
事件分發
在瞭解事件分發前,應該先了解一下Activity的檢視層級,因為這裡只涉及事件的分發,所以做了一個比較簡單的示意圖:
Window、Activity、DecorView以及ViewRoot之間的關係
首先需要明確一點的事,事件分發機制實際上應用了責任鏈模式。
事件分發的大致流程如下圖所示:
具體的說說明可以參考這篇文章:安卓自定義View進階-事件分發機制原理
接下來只是整理一些筆記(參考《Android開發藝術探索》):
(1)相關方法是說明
dispatchTouchEvent
方法用來進行事件分發,如果事件能夠傳給當前View,此方法一定會被呼叫,返回結果受當前View的onTouchEvent與下級View的dispatchTouchEvent方法的影響,表示是否消耗當前事件。
onInterceptTouchEvent
在dispatchTouchEvent的內部呼叫,用來判斷是是否攔截某個事件,如果當前ViewGroup(View不存在該方法,且在ViewGroup中預設返回不攔截)攔截了某個事件,那麼在用一個事件序列當中,此方法不會被再次呼叫,返回結果表示是否攔截當前事件。
(同一事件序列是指從手指接觸螢幕的那一個刻起,到手指離開螢幕的那一刻結束,在這個過程中所產生的一系列事件,
這個時間序列以DOWN事件開始,中間含有數量不定的MOVE事件,最終以UP事件結束)
onTouchEvent
在dispatchTouchEvent的內部呼叫,用來處理點選事件,返回結果表示是否消耗了當前事件,如果不消耗,則在同一個事件序列中,當前View無法再次接收到事件。
(onTouchEvent預設消耗事件,即返回true,除非它是不可點選的,clickable和longClickable同時為false,其中longClickeable都預設為false,clickable則分情況,Button的預設為true,TextView的為false。而View的enable屬性不影響onTouchEvent的預設返回值,自只要clickable和longClickable有一個為true即可)
根據上述描述可以知道:
如果一個View攔截了一個事件序列的某一個事件,則該事件後的事件都會預設被攔截。
可以知道如果一個View不消耗(消耗即返回true,但不代表一定要根據該事件實現某種邏輯,可以就是空邏輯)一個事件序列中的某一事件,那麼接下來的一連串事件也就無法接收到了,因為事件一般是按序列來的,不先消耗前一個事件,則無法繼續後序事件的處理。
(2)事件分發的巨集觀流程
一個事件的大概流程如下:
Activity -> Window(由PhoneWindow實現) -> DecorView(底層容器)-> RootView(通過setConentView設定的)
PhoneWindow(附屬於Activity的,控制頂級View的外觀和行為策略)和DecorView只負責事件的分發,不存在攔截與處理事件的邏輯。如果事件最終沒有被消耗,就會返回給Activity,由其onTouchEvent消耗。
(3)當一個View需要處理事件時,如果它設定了OnTouchListner,那麼OnTouchListener中的onTouch方法會被回撥。這時事件如何處理還需要看onTouch的返回值,如果返回false,則當前View的onTouchEvent方法會被呼叫,否則不會。給View設定的OnTounchListener優先順序大於onTouchEvent。(OnTouchListenr是從外部定義處理TouchEvent的事件的邏輯,onTouchEvent的邏輯則是事先被定義好的)。在onTouchEvent中,如果當前有設定OnClickListener,那麼它的onClick方法會被呼叫(前提是當前View是可點選的,且它收到了down和up事件),OnClickListener的優先順序最低。
(4)正常情況下,一個事件序列只能被一個View攔截且消耗。因為一旦一個元素攔截了某些事件,那麼同一個事件序列的後續事件都會交給它處理,且其onInterceptTouchEvent不會再被呼叫,從而不能在傳遞給其它View處理。但是可以通過特殊手段做到,如一個View將本該自己處理的事件通過onTouchEvent強行傳遞給其它View處理。
(5)如果View不消耗除ACTION_DOWN以外的事件,那麼這個事件序列將會消失,此時父元素的onTouchEvent不會被呼叫(因為父元素認為將事件傳遞給了子元素去處理),但是當前View可以持續收到後續事件,最後消失的事件會回傳給Activity處理。
(6)通過requestDisallowInterceptTouchEvent方法可以在子元素中干預父元素的事件分發過程,但是DOWN事件除外。
(7)事件傳遞給子元素,需要先判斷子元素是否能夠接收到點選事件:子元素是否在播放動畫和點選事件的座標是否落在子元素的區域內。
滑動衝突
滑動衝突的場景可以簡單分為以下三種:
- 外部滑動方向和內部不一致
- 外部滑動方向與內部一致
- 上面兩種情況的巢狀
解決方法
(1)外部攔截法
點選事件都先經過父容器的攔截處理,攔截住需要事件,防止傳遞到子元素。比較符合事件分發的機制,需要重寫父容器的onInterceptTouchEvent方法,在內部做出相應攔截即可。
(2)內部攔截法
父容器不攔截任何事件,所有事件都交給子元素,如果子元素需要就直接消耗掉,否則交由父容器處理。需要配合requsestDisallowTouchEvent方法才能正常工作。除了子元素需要處理以外,父容器也要預設攔截除了DOWN意外的其他事件。
相關文章
- 【朝花夕拾】Android自定義View篇之(七)Android事件分發機制(下)解決滑動衝突AndroidView事件
- Android View 滑動衝突解決方式以及原理AndroidView
- android事件分發機制詳解Android事件
- Android事件分發機制Android事件
- 一種巢狀滑動衝突的解決方案巢狀
- Android事件傳遞、多點觸控及滑動衝突的處理Android事件
- Android事件分發機制,你瞭解過嗎?Android事件
- Android 事件分發機制的理解Android事件
- Android的MotionEvent事件分發機制Android事件
- Android事件分發機制三:事件分發工作流程Android事件
- 一種非巢狀滑動衝突的解決方案巢狀
- Android com.android.support衝突解決Android
- Android 事件分發機制原始碼詳解-最新 APIAndroid事件原始碼API
- View 體系詳解:座標系、滑動、手勢和事件分發機制View事件
- Android View 的事件體系 -- 事件分發機制AndroidView事件
- 淺談Android 事件分發機制(二)Android事件
- Android事件分發機制簡單理解Android事件
- Android事件分發:從原始碼角度分析View事件分發機制Android事件原始碼View
- RecyclerView 、ViewPager 左右滑動衝突Viewpager
- Android 解決BottomSheetDialog 拖曳衝突問題Android
- 淺談Android中的事件分發機制Android事件
- 【Android基礎】講講Android的事件分發機制Android事件
- Android 事件分發機制原始碼解析-view層Android事件原始碼View
- 面試:講講 Android 的事件分發機制面試Android事件
- git 解決衝突Git
- Git 解決衝突Git
- com.android.support衝突的解決辦法Android
- Android從原始碼角度剖析View事件分發機制Android原始碼View事件
- 基於原始碼分析 Android View 事件分發機制原始碼AndroidView事件
- Android自定義View之事件分發機制總結AndroidView事件
- kotlin語言:解決drawerLayout與viewpager的衝突、NavigationView側滑裡面menu的點選事件KotlinViewpagerNavigation事件
- 如何解決 touchstart 事件與 click 事件的衝突事件
- 解決Flutter的ListView巢狀ListView滑動衝突以及無限高度問題FlutterView巢狀
- Elasticsearch——併發衝突以及解決方案Elasticsearch
- 每日一問:Android 滑動衝突,你們都是怎樣處理的Android
- android觸控事件分發機制,曾困惑你我的地方Android事件
- Android事件分發機制:基礎篇:最全面、最易懂Android事件
- Android事件分發機制五:面試官你坐啊Android事件面試
- View事件分發機制分析View事件