Android事件分發(fā)機(jī)制

參考:
更簡單的學(xué)習(xí)Android事件分發(fā)

1. 相關(guān)方法

相關(guān)方法 方法功能 Activity ViewGroup View
dispatchTouchEvent() 事件分發(fā) Yes Yes Yes
onInterceptTouchEvent() 事件攔截 No Yes No
onTouchEvent() 事件消費(fèi) Yes Yes Yes

事件分發(fā): dispatchTouchEvent

  • 返回true,則表示在當(dāng)前View或其子View(子子...View)中,找到了處理事件的View;
  • 返回false,則表示在當(dāng)前View或其子View(子子...View)中沒有處理事件的View;

事件攔截: onInterceptTouchEvent

  • 返回true,則表示當(dāng)前View攔截這個(gè)事件并交予當(dāng)前View的onTouchEvent()處理,不會再向子View傳遞這個(gè)事件;
  • 返回false,則表示當(dāng)前View不攔截這個(gè)事件,只有他的子View的dispatchTouchEvent()返回false,才會分發(fā)到當(dāng)前View的onTouchEvent()方法,否則當(dāng)前View不處理這個(gè)事件

事件消費(fèi): onTouchEvent

  • 返回true,則表示當(dāng)前View消費(fèi)掉這個(gè)事件;
  • 返回false,則表示當(dāng)前View不消費(fèi)這個(gè)時(shí)間;

2. 具體分析

首先,我們來說一下觸摸事件(不考慮多點(diǎn)觸摸),一組觸摸時(shí)間以一個(gè)DOWN事件開始,后跟0個(gè)或多個(gè)MOVE事件,最后跟一個(gè)UP或CANCEL事件。事件分發(fā)是從外往內(nèi)分發(fā)的或者說從上到下,即事件從父層View向子層View分發(fā)。


布局

接下來我們根據(jù)上圖來分析以下五種基本情況,復(fù)雜的事件分發(fā)一般都是這幾種情況的組合,掌握了基本事件的分發(fā)機(jī)制,復(fù)雜的也就迎刃而解了:

  • View消費(fèi)DOWN、MOVE、UP事件
  • View不消費(fèi)DOWN事件
  • View不消費(fèi)MOVE、UP事件
  • ViewGroup攔截DOWN事件
  • ViewGroup攔截MOVE、UP事件

一般情況下DOWN和MOVE、UP的分發(fā)機(jī)制不同,所以我們要分開討論(當(dāng)View消費(fèi)觸摸事件時(shí),事件的分發(fā)一致,所以我們可合并分析)

1). 事件分發(fā)(View消費(fèi))

事件傳遞過程:

DOWN事件:

從Activity#dispatchTouchEvent開始分發(fā),傳遞到ViewGroup#dispatchTouchEvent,再傳遞到ViewGroup#onInterceptTouchEvent,ViewGroup#onInterceptTouchEvent返回false表示不攔截,所以事件再往下分發(fā)到View#dispatchTouchEvent,繼而傳遞到View#onTouchEvent,View#onTouchEvent返回true表示消費(fèi)掉這個(gè)事件,返回的結(jié)果true依次通過View#dispatchTouchEvent、ViewGroup#dispatchTouchEvent再返回給Activity#dispatchTouchEvent表示這個(gè)事件被消費(fèi)掉了,后續(xù)的MOVE、UP事件都將傳遞給View#onTouchEvent處理。

MOVE、UP事件:

與DOWN事件相同。

通過上面的傳遞過程,我們可以得出:

  • onInterceptTouchEvent返回false表示不攔截事件,事件將繼續(xù)向下分發(fā),傳遞到它子View的dispatchTouchEvent中
  • onTouchEvent返回true表示消費(fèi)這個(gè)事件,返回結(jié)果true將通過dispatchTouchEvent以及父View中的dispatchTouchEvent傳遞回Activity,那么這個(gè)事件序列中的后續(xù)事件都將交給它處理,

注:
View如果需要處理觸摸事件必須(clickable == true || longClickable == true),否則將不會分發(fā)事件給View

2). 事件分發(fā)(View不消費(fèi))

事件傳遞過程:

DOWN事件:

從Activity#dispatchTouchEvent開始分發(fā),傳遞到ViewGroup#dispatchTouchEvent,再傳遞到ViewGroup#onInterceptTouchEvent,ViewGroup#onInterceptTouchEvent返回false表示不攔截,所以事件再往下分發(fā)到View#dispatchTouchEvent,繼而傳遞到View#onTouchEvent,View#onTouchEvent返回false表示不消費(fèi)這個(gè)事件,返回的結(jié)果false再傳遞給View#dispatchTouchEvent,ViewGroup#dispatchTouchEvent得知View不處理這個(gè)事件所以再把事件交給ViewGroup#onTouchEvent,ViewGroup#onTouchEvent返回false表示自己也不處理這個(gè)事件,并把結(jié)果false反回給ViewGroup#dispatchTouchEvent,Activity#dispatchTouchEvent得知ViewGroup不處理這個(gè)事件所以再把這個(gè)事件交給Activity#onTouchEvent處理,Activity#onTouchEvent也返回false表示自己也不處理這個(gè)事件,最后這個(gè)結(jié)果false傳回了Activity#dispatchTouchEvent中,表示找了一圈這個(gè)事件沒人消費(fèi)。

MOVE、UP事件:

因?yàn)镈OWN事件沒人處理,所以這個(gè)事件序列中的后續(xù)事件MOVE、UP就不再分發(fā)了,而是直接交給Activity#onTouchEvent處理,Activity#onTouchEvent返回false表示自己不處理。

通過上面的傳遞過程,我們可以得出:

  • 如果onTouchEvent返回false表示不處理這個(gè)事件,這個(gè)事件則會向上傳給父View的onTouchEvent中,如果都不處理則會傳遞回Activity#onTouchEvent中
  • 如果View或ViewGroup不消費(fèi)DOWN事件,那么這個(gè)序列的后續(xù)事件MOVE、UP將不會再傳遞給它處理
3). 事件攔截

事件傳遞過程:

DOWN事件:

從Activity#dispatchTouchEvent開始分發(fā),傳遞到ViewGroup#dispatchTouchEvent,再傳遞到ViewGroup#onInterceptTouchEvent,ViewGroup#onInterceptTouchEvent返回true表示攔截,所以事件傳遞給了ViewGroup#onTouchEvent,并不會再傳給View,ViewGroup#onTouchEvent返回true表示處理這個(gè)事件,并返回給ViewGroup#dispatchTouchEvent,ViewGroup#dispatchTouchEvent把結(jié)果true返回給Activity#dispatchTouchEvent表示找到處理這個(gè)事件的View了。

MOVE、UP事件:

這個(gè)事件序列中的后續(xù)事件MOVE、UP分發(fā)到ViewGroup#dispatchTouchEvent是會判斷(actionMasked == MotionEvent.ACTION_DOWN || mFirstTouchTarget != null),因?yàn)榧炔皇荄OWN事件,子View中有沒有處理到DOWN事件所以mFirstTouchTarget == null就不再傳遞給ViewGroup#onInterceptTouchEvent,而是直接傳給ViewGroup#onTouchEvent,ViewGroup#onTouchEvent返回true表示處理這個(gè)事件,并返回給ViewGroup#dispatchTouchEvent,ViewGroup#dispatchTouchEvent把結(jié)果true返回給Activity#dispatchTouchEvent表示找到處理這個(gè)事件的View了。

通過上面的傳遞過程,我們可以得出:

  • onInterceptTouchEvent返回為true表示攔截事件,事件將直接傳遞給它自己的onTouchEvent處理,不會再傳遞給子View
  • DOWN事件被當(dāng)前ViewGroup的onInterceptTouchEvent攔截,這個(gè)序列的后續(xù)事件MOVE、UP將不會傳遞給它的onInterceptTouchEvent,而直接傳遞給它的onTouchEvent方法處理

注:
1.如果DOWN事件的傳遞給onInterceptTouchEvent時(shí)沒有攔截,MOVE事件傳遞給onInterceptTouchEvent時(shí)攔截,這樣將傳給View一個(gè)CANCEL事件,之后的MOVE事件就不再傳遞給View,而直接傳遞給ViewGroup#onTouchEvent處理。
2.子View中可以調(diào)用getParent().requestDisallowInterceptTouchEvent(true);來阻止父View攔截觸摸事件,父View就不會再調(diào)用onInterceptTouchEvent攔截事件了。

3. 總結(jié)

  • 如果某個(gè)View消費(fèi)了DOWN事件,那么通常情況下后續(xù)的MOVE、UP事件也將交給它處理,但是可以調(diào)用父View的onInterceptTouchEvent進(jìn)行攔截
  • 如果某個(gè)View不消費(fèi)DOWN事件,那么這個(gè)系列后續(xù)事件也不會交給它處理
  • 一個(gè)事件如果都沒有View消費(fèi),那么將由Activity處理
  • 如果事件處理的結(jié)果為true,那么結(jié)果會不斷返回給父View#dispatchTouchEvent方法,知道傳遞回Activity
  • 如果事件傳遞的結(jié)果為false,回傳的結(jié)果不斷調(diào)用父View#onTouchEvent方法,直到找到消費(fèi)事件的View或者傳遞回Activity
  • View如果需要處理觸摸事件必須(clickable == true || longClickable == true)
    ,否則將不會分發(fā)事件給View
  • 如果DOWN事件的傳遞給onInterceptTouchEvent時(shí)沒有攔截,MOVE事件傳遞給onInterceptTouchEvent時(shí)攔截,這樣將傳給View一個(gè)CANCEL事件,之后的MOVE事件就不再傳遞給View,而直接傳遞給ViewGroup#onTouchEvent處理。
  • 子View中可以調(diào)用getParent().requestDisallowInterceptTouchEvent(true);來阻止父View攔截觸摸事件,父View就不會再調(diào)用onInterceptTouchEvent攔截事件了。
  • onInterceptTouchEvent返回為true表示攔截事件,事件將直接傳遞給它自己的onTouchEvent處理,不會再傳遞給子View
  • DOWN事件被當(dāng)前ViewGroup的onInterceptTouchEvent攔截,這個(gè)序列的后續(xù)事件MOVE、UP將不會傳遞給它的onInterceptTouchEvent,而直接傳遞給它的onTouchEvent方法處理

本人技術(shù)有限,歡迎指正,謝謝!

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,117評論 6 537
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,860評論 3 423
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事?!?“怎么了?”我有些...
    開封第一講書人閱讀 177,128評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,291評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 72,025評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,421評論 1 324
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,477評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,642評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,177評論 1 335
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,970評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 43,157評論 1 371
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,717評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,410評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,821評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,053評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,896評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 48,157評論 2 375

推薦閱讀更多精彩內(nèi)容