Android的Touch事件處理機(jī)制介紹
Android的Touch事件處理分3個(gè)層面:Activity層,ViewGroup層,View層。
首先說一下Touch事件處理的幾條基本規(guī)則。
如果在某個(gè)層級沒有處理ACTION_DOWN事件,那么該層就再也收不到后續(xù)的Touch事件了直到下一次ACTION_DOWN事件。
說明:
a.某個(gè)層級沒有處理某個(gè)事件指的是它以及它的子View都沒有處理該事件。
b.這條規(guī)則不適用于Activity層(它是頂層),它們可以收到每一個(gè)Touch事件。
c.如果沒有處理ACTION_MOVE這類事件,不會(huì)有任何影響。
如果ACTION_DOWN事件發(fā)生在某個(gè)View的范圍之內(nèi),則后續(xù)的ACTION_MOVE,ACTION_UP和ACTION_CANCEL等事件都將被發(fā)往該View,即使事件已經(jīng)出界了。
第一根按下的手指觸發(fā)ACTION_DOWN事件,之后按下的手指觸發(fā)ACTION_POINTER_DOWN事件,中間起來的手指觸發(fā)ACTION_POINTER_UP事件,最后起來的手指觸發(fā)ACTION_UP事件(即使它不是觸發(fā)ACTION_DOWN事件的那根手指)。
pointer id可以用于跟蹤手指,從按下的那個(gè)時(shí)刻起pointer id生效,直至起來的那一刻失效,這之間維持不變。
如果一個(gè)ACTION_DOWN事件被父View攔截了,則任何子View不會(huì)再收到任何Touch事件了(這符合第1點(diǎn)的要求)。
如果一個(gè)非ACTION_DOWN事件被父View攔截了,則那些上次處理了ACTION_DOWN事件的子View會(huì)收到一個(gè)ACTION_CANCEL事件,之后不會(huì)再收到任何Touch事件了,即使父View不再攔截后續(xù)的Touch事件。
如果父View決定處理Touch事件或者子View沒有處理Touch事件,則父View按照普通View的處理方式處理Touch事件,否則它根本不處理Touch事件(它只負(fù)責(zé)分發(fā))。
如果父View在onInterceptTouchEvent中攔截了事件,則onInterceptTouchEvent中不會(huì)再收到Touch事件了,事件被直接交給它自己處理(按照普通View的處理方式)。
下面分層講述一些細(xì)節(jié)。
1.Activity層:
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
onUserInteraction();
}
if (getWindow().superDispatchTouchEvent(ev)) { //在這里交給View層處理
return true;
}
return onTouchEvent(ev); // 如果View層沒有處理,則在這里處理
}
2.View層:
public boolean dispatchTouchEvent(MotionEvent event) {
// 省略了部分細(xì)節(jié)
ListenerInfo li = mListenerInfo;
if (li != null && li.mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
&& li.mOnTouchListener.onTouch(this, event)) {
return true;
}
if (onTouchEvent(event)) {
return true;
}
return false;
}
View的onTouch方法代碼比較多,主要的邏輯分兩步:先是將事件交給TouchDelegate處理(如果有的話),如果TouchDelegate沒有處理再自行處理;自行處理主要負(fù)責(zé)View狀態(tài)的變換(如按下狀態(tài)),長按事件,點(diǎn)擊事件的檢測與觸發(fā)等。
3.ViewGroup層(比較復(fù)雜):
ViewGroup層處理Touch事件的總體邏輯是:先檢測是否需要攔截,沒有攔截的話下發(fā)給子View處理,如果子View沒有處理再自行處理,自行處理的邏輯與View一樣。
攔截的邏輯是,將從down到up之間的所有事件看作一組事件,如果從down就攔截了,則組內(nèi)的后續(xù)其它事件完全交給自己處理,不需要再進(jìn)入攔截邏輯了;如果是從中間攔截,則先給子View發(fā)送cancel事件,組內(nèi)的后續(xù)其它事件完全交給自己處理,不需要再進(jìn)入攔截邏輯了。
分發(fā)的邏輯是,在ACTION_DOWN事件的時(shí)候,尋找子View進(jìn)行處理,稱為尋找Target;如果沒有找到Target,則自行處理;如果找到Target,則交由Target處理。
從代碼上看,dispatchTouchEvent負(fù)責(zé)分發(fā)邏輯,onTouchEvent負(fù)責(zé)真正的處理邏輯,一般應(yīng)該重載onTouchEvent,只有特殊情況下才需要重載dispatchTouchEvent。
相關(guān)文章
Android實(shí)現(xiàn)購物車添加商品動(dòng)畫
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)購物車添加商品動(dòng)畫,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06Android使用Gradle依賴配置compile、implementation與api的區(qū)別介紹
這篇文章主要給大家介紹了關(guān)于Android使用Gradle依賴配置compile、implementation與api區(qū)別的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起看看吧2018-09-09Android開發(fā)中MJRefresh自定義刷新動(dòng)畫效果
本文給大家介紹了MJRefresh自定義刷新動(dòng)畫效果,包括常見用法等相關(guān)知識(shí),非常不錯(cuò),具有參考借鑒價(jià)值,感興趣的朋友一起看看吧2016-11-11Flutter使用JsBridge方式處理Webview與H5通信的方法
這篇文章主要介紹了Flutter使用JsBridge方式處理Webview與H5通信的方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-04-04淺談Android開發(fā)中ListView控件性能的一些優(yōu)化方法
這篇文章主要介紹了Android開發(fā)中ListView控件性能的一些優(yōu)化方法,需要的朋友可以參考下2016-01-01去掉RecycleView或者ListView上下滑動(dòng)陰影的方法
下面小編就為大家分享一篇去掉RecycleView或者ListView上下滑動(dòng)陰影的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-01-01Android服務(wù)應(yīng)用ClockService實(shí)現(xiàn)鬧鐘功能
這篇文章主要為大家詳細(xì)介紹了Android服務(wù)應(yīng)用ClockService實(shí)現(xiàn)鬧鐘功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11Android 單例模式的四種實(shí)現(xiàn)方式
單例模式作為設(shè)計(jì)模式之一,使用場景非常多。本文講述了Android實(shí)現(xiàn)單例模式的幾種方式2021-05-05Android實(shí)現(xiàn)EditText內(nèi)容保存為Bitmap的方法
這篇文章主要介紹了Android實(shí)現(xiàn)EditText內(nèi)容保存為Bitmap的方法,涉及Android中saveBitmap方法的簡單使用技巧,需要的朋友可以參考下2016-01-01