事件分發(fā)在 android 中實在是太重要了,滑動沖突的前置基礎(chǔ)知識,滑動沖突不會處理,那么大部分頁面效果是寫不出來的,尤其是時下越來越多的聯(lián)合滑動的頁面效果,典型的就是 behavior 了,其他的像是典型的滑動控件嵌套沖突,左滑退出,列表側(cè)滑菜單,下拉刷新,上拉加載都是需要我們對 android 事件分發(fā)有深刻理解的。
事件分發(fā)誰來控制
android 的頁面結(jié)構(gòu)是這樣的,典型的樹形結(jié)構(gòu)


每一個 Actvity 都有一個 window ,window 所有視圖的最頂層容器,視圖的外觀和行為都?xì)w他管,不論是背景顯示,標(biāo)題欄還是事件處理都是他管理。
一般我們用不到去操作 window ,但是觸控事件的接收和分發(fā)是由 window 來執(zhí)行的,window 會根據(jù)視圖樹的順序,從外向里層層傳遞觸控事件
所以觸控事件是由 window 接收并分發(fā)的。
事件分發(fā)4大方法
- dispatchTouchEvent 事件分發(fā)
- onInterceptTouchEvent 事件攔截
- onTouchEvent 事件處理
- requestDisallowInterceptTouchEvent(true)
ViewGroup 4個方法都有,View 沒有事件攔截方法。這4個方法就是 Android 事件分發(fā)和處理的具體方法了,我們在實際處理時都是重寫這4個方法。
這3個方法都有一個 boolean 的返回值,onInterceptTouchEvent 方法表示我要攔截這個事件,onTouchEvent 表示我處理了事件
dispatchTouchEvent
dispatchTouchEvent 方法作為 view 的事件處理的 API 入口,內(nèi)部會調(diào)用 onInterceptTouchEvent 和 onTouchEvent 來計算返回的 boolean 值,一般我們都不會動 dispatchTouchEvent 方法,因為沒有意思。但是我們直接返回 true 時,表示我消費這個事件了onInterceptTouchEvent
onInterceptTouchEvent 方法默認(rèn)是返回 false 的,表示不會攔截這個事件,但是若是返回 true 則表示這個事件我攔截住了,就不再往我下一級 view 傳遞了,然后會把事件交給自己的 onTouchEvent 方法onTouchEvent
onTouchEvent 方法可以獲取具體的觸控參數(shù),進行手勢方向,當(dāng)前手勢類型判斷,可以記錄觸摸點的 x,y 坐標(biāo)。onTouchEvent 方法若是返回 true ,則表示這個事件我已經(jīng)處理過了,那么整個事件傳遞過程就結(jié)速了,否則的話這個事件會原路返回,去一級一級的跑上一層 view 的 onTouchEvent 方法,直到有人返回 true 或是沒有了。requestDisallowInterceptTouchEvent
view.getParent().requestDisallowInterceptTouchEvent(true) ,可以讓內(nèi)層的 view 獲取到事件,而忽略上層視圖的攔截。true 表示接受事件,fasle 表示不接受事件。原理分析看這里:
事件分發(fā)的順序
總的來說,事件是由其父視圖向子視圖傳遞,按前面的圖來說是這樣。
Activity -> PhoneWindow -> DecorView -> ViewGroup -> ... -> View
- window 根據(jù)視圖樹順序,從外到內(nèi)遍歷所有的 view ,詢問每一個遍歷出來的 view 你要不要消費觸摸事件
- 每個 view 會跑自己的 dispatchTouchEvent 方法,判斷自己是否需要消費這個事件
- dispatchTouchEvent 方法首先會詢問 onInterceptTouchEvent 方法,是否要攔截這個事件。
- 若是攔截則會跑自己的 onTouchEvent 方法,若不攔截則會傳遞給下一級 view,調(diào)用下一級 view 的 dispatchTouchEvent 方法
- onTouchEvent 若是返回 true ,表示事件實際處理過了,那么整個事件傳遞在這個節(jié)點就結(jié)速了,不再傳遞了。若是返回 fasle,這個事件會原路返回,去一級一級的跑上一層 view 的 onTouchEvent 方法,直到有人返回 true 或是沒有了。



這個點建議大家去看經(jīng)典的文章
前人的經(jīng)典文章我就不逾越了,推薦大家看經(jīng)典,省的理解錯誤,這里我放地址
具體的例子我就不寫了,大家看這篇文章的例子,設(shè)計的很全面
想了解的觸控類型和參數(shù)還有源碼分析的看這里
想更詳細(xì)了解 MotionEvent 看這里,很詳細(xì)包括多點觸控
我復(fù)制一下核心要點:
- 事件分發(fā)原理: 責(zé)任鏈模式,事件層層傳遞,直到被消費。
- View 的 dispatchTouchEvent 主要用于調(diào)度自身的監(jiān)聽器和 onTouchEvent。
- View的事件的調(diào)度順序是 onTouchListener > onTouchEvent > onLongClickListener > onClickListener 。
- 不論 View 自身是否注冊點擊事件,只要 View 是可點擊的就會消費事件。
- 事件是否被消費由返回值決定,true 表示消費,false 表示不消費,與是否使用了事件無關(guān)。
- ViewGroup 中可能有多個 ChildView 時,將事件分配給包含點擊位置的 ChildView。
- ViewGroup 和 ChildView 同時注冊了事件監(jiān)聽器(onClick等),由 ChildView 消費。
- 一次觸摸流程中產(chǎn)生事件應(yīng)被同一 View 消費,全部接收或者全部拒絕。
- 只要接受 ACTION_DOWN 就意味著接受所有的事件,拒絕 ACTION_DOWN 則不會收到后內(nèi)容。
- 如果當(dāng)前正在處理的事件被上層 View 攔截,會收到一個 ACTION_CANCEL,后續(xù)事件不會再傳遞過來。