Android自定義View之事件分發(fā)機制總結(jié)

Android自定義View系列

事件序列

(1)手指接觸屏幕后會產(chǎn)生一系列事件灯变,事件分為3種:ACTION_DOWN(手指剛剛接觸屏幕)养叛、ACTION_MOVE(手指在屏幕移動)洲押、ACTION_UP(手指從屏幕松開)

(2)一個事件序列為ACTION_DOWN-->ACTION_MOVE-->...-->ACTION_UP

事件傳遞的順序

Activity-->Window-->decor view-->我們的layout匈辱,ViewGroup-->我們布局中被點擊的子View

如果我們的子View沒有處理事件,那事件就會反向向上傳遞回來:

我們布局中被點擊的子View-->上層的ViewGroup-->decor view-->Window-->Activity

如果所有的View都沒有消耗事件,那最后事件會傳回到Activity断楷,由Activity處理(Activity的onTouchEvent()方法被調(diào)用)

三大方法

ViewGroup中有3個跟事件分發(fā)有關(guān)的方法,分別是 dispatchTouchEvent崭别、 onInterceptTouchEvent冬筒、onTouchEvent。

(1)dispatchTouchEvent方法

dispatchTouchEvent方法用來進行事件的分發(fā)茅主。事件傳遞到當(dāng)前View時舞痰,這個方法就會被調(diào)用。dispatchTouchEvent方法里面包含了具體的事件分發(fā)邏輯诀姚,返回結(jié)果受當(dāng)前View的onTouchEvent方法和下級View的dispatchTouchEvent方法的影響响牛。

(2)onInterceptTouchEvent方法

onInterceptTouchEvent方法在dispatchTouchEvent方法內(nèi)部被調(diào)用,用來判斷是否攔截某個事件赫段。如果當(dāng)前View攔截了某個事件呀打,那么在同一個事件序列當(dāng)中,此方法不會被再次調(diào)用糯笙,返回結(jié)果表示是否攔截當(dāng)前事件贬丛。這個方法只有VewGroup中有,View中沒有给涕。

(3)onTouchEvent方法

在dispatchTouchEvent方法中調(diào)用豺憔,用來處理點擊事件额获,返回結(jié)果表示是否消耗當(dāng)前事件,如果不消耗恭应,則在同一個事件序列中咪啡,當(dāng)前View無法再次接收到事件。

onTouchListener暮屡、onTouchEvent撤摸、onClickListener的優(yōu)先級

(1)onTouchListener和onTouchEvent都在dispatchTouchEvent方法中被調(diào)用,onClickListener在onTouchEvent方法中被調(diào)用

(2)onTouchListener的優(yōu)先級高于onTouchEvent方法褒纲,如果onTouchListener的onTouch方法返回true准夷,則onTouchEvent方法不會被調(diào)用,當(dāng)然onClickListener就更不會被調(diào)用了

(3)在onTouchEvent方法中莺掠,如果當(dāng)前View設(shè)置了onClickListener衫嵌,那么onClickListener的onClick方法會被調(diào)用

(4)只要View的CLICKABLE和LONKG_CLICKABLE有一個為true,View就會消耗當(dāng)前事件彻秆,也就是說onTouchEvent方法最后會返回true楔绞。

(5)View的LONG_CLICKABLE屬性默認(rèn)為false,而CLICKABLE屬性和具體的View有關(guān)唇兑,可點擊的View的CLICKABLE屬性為true酒朵,不可點擊的View的CLICKABLE屬性為false。

ViewGroup中的事件分發(fā)邏輯

ViewGroup中的事件分發(fā)邏輯可以用一段偽代碼來表述

public boolean dispatchTouchEvent(MotionEvent ev) {
    boolean consume = false;
    if (onInterceptTouchEvent(ev)) {
        consume = onTouchEvent();
    }else {
        consume = child.dispatchTouchEvent(ev);
    }
    
    return consume;
}

從上述的偽代碼中我們可以總結(jié)出ViewGroup中的事件分發(fā)流程:

(1)事件傳遞到ViewGroup時扎附,dispatchTouchEvent方法會被調(diào)用蔫耽。如果這個ViewGroup的onInterceptTouchEvent方法返回true,則表示它要攔截事件留夜,事件就會交給當(dāng)前ViewGroup的onTouchEvent方法處理匙铡。

(2)如果當(dāng)前ViewGroup的onInterceptTouchEvent返回false,即不攔截事件碍粥,則會調(diào)用子元素的dispatchTouchEvent方法鳖眼,這樣就把事件傳遞給了子元素。

(3)如果子元素沒有消耗事件嚼摩,也就是子元素的dispatchTouchEvent方法返回false钦讳,那事件會由當(dāng)前ViewGroup自己處理,當(dāng)前ViewGroup的onTouchEvent會被調(diào)用低斋。如果當(dāng)前ViewGroup的dispatchTouchEvent方法也返回false蜂厅,最后就會一層層往上,如果事件一直沒有被消耗膊畴,那么最后Activity的onTouchEvent方法會被調(diào)用

(4)這里需要理解一下的是ViewGroup繼承自View掘猿,ViewGroup中并沒有onTouchEvent方法。在所有子元素沒有消耗事件時唇跨,ViewGroup會調(diào)用父類稠通,也就是View的dispatchTouchEvent方法衬衬,從而調(diào)用到onTouchEvent方法來自己處理事件,如果自己沒有消耗事件改橘,dispatchTouchEvent方法就會返回false滋尉,從而將事件反向往上層傳遞。

(5)如果ACTION_DOWN事件子元素沒處理(onTouchEvent返回false)飞主,那這個事件序列的其他事件(MOVE和UP事件)都不會再分派給子元素處理狮惜。

(6)ViewGroup默認(rèn)不攔截任何事件

(7)對于ACTION_DOWN事件,ViewGroup每次都會調(diào)用onInterceptTouchEvent方法來判斷是否需要攔截事件碌识,一旦確定要攔截事件碾篡,后續(xù)的ACTION_MOVE和ACTION_UP事件都ViewGroup自己處理,不會傳遞給子View筏餐,也不會再調(diào)用onInterceptTouchEvent方法开泽。所以onInterceptTouchEvent方法不是每次事件都會被調(diào)用的。

(8)子View可以通過requestDisallowInterceptTouchEvent方法來干預(yù)父元素的除了ACTION_DOWN意外的事件分發(fā)過程

View中的事件分發(fā)邏輯

requestDisallowInterceptTouchEvent方法

requestDisallowInterceptTouchEvent方法用于影響父元素的事件攔截策略魁瞪,requestDisallowInterceptTouchEvent(true)穆律,表示不允許父元素攔截事件,這樣事件就會傳遞給子View导俘。一般這個方法子View用的多峦耘,可以用來處理滑動沖突問題。

事件分發(fā)邏輯

(1)View中沒有onInterceptTouchEvent方法趟畏,所以一旦事件傳遞到View贡歧,那么View的dispatchTouchEvent方法就會被調(diào)用滩租。

(2)dispatchTouchEvent方法中處理事件的邏輯順序是onTouchListener-->onTouchEvent-->onClickListener赋秀。

(3)也就是說如果View設(shè)置了onTouchListener,那onTouchListener的onTouch方法會被調(diào)用律想,如果onTouch方法返回true猎莲,那事件就被消耗了,事件分發(fā)結(jié)束技即,onTouchEvent不會被調(diào)用著洼。

(4)如果onTouch方法返回false,那么onTouchEvent就會被調(diào)用而叼。如果View設(shè)置了onClickListener身笤,當(dāng)ACTION_UP事件到來時,onTouchEvent中的onClickListener的onClick方法也會被調(diào)用葵陵。

(5)View一般都會消耗事件液荸,如果View沒有消耗ACTION_DOWN事件,那后面ACTION_MOVE和ACTION_UP就都不會傳遞給View脱篙。

常用的滑動沖突處理邏輯

(1)利用父布局的onInterceptTouchEvent方法

這個思路就是在父布局需要處理事件時攔截下來娇钱,其他時候不攔截伤柄。有幾個注意點:

  • 對于ACTION_DOWN事件,onInterceptTouchEvent方法必須返回false文搂,因為一旦返回true适刀,子元素永遠(yuǎn)也接收不到事件了,那還解決個毛線沖突煤蹭。
  • 主要的邏輯就在ACTION_MOVE的處理上笔喉,需不需要攔截的邏輯在這里根據(jù)需要來實現(xiàn)
  • 對于ACTION_UP事件返回false,因為一旦父元素返回true硝皂,那子View就接受不到ACTION_UP事件了然遏,也就無法觸發(fā)onClick事件。

(2)利用子View的requestDisallowInterceptTouchEvent方法

這個思路就是父布局默認(rèn)攔截除了ACTION_DOWN的所有事件吧彪,子View中在dispatchTouchEvent方法中根據(jù)需要來干預(yù)父布局的攔截策略待侵。默認(rèn)不允許父布局?jǐn)r截事件,在需要父布局處理事件時姨裸,通過requestDisallowInterceptTouchEvent(false)方法讓父布局處理事件秧倾,其他時候都由子View處理。

注意點:

  • 同樣的對于ACTION_DOWN事件傀缩,onInterceptTouchEvent方法必須返回false那先,其他事件默認(rèn)返回true
  • 在子View的dispatchTouchEvent方法中,對于ACTION_DOWN事件赡艰,通過調(diào)用requestDisallowInterceptTouchEvent(true)默認(rèn)不允許父布局?jǐn)r截事件售淡,這樣后續(xù)事件都交給子View處理
  • 在子View的dispatchTouchEvent方法中,對于ACTION_MOVE事件慷垮,默認(rèn)是子View處理揖闸,在需要父布局處理時,調(diào)用requestDisallowInterceptTouchEvent(false)方法來讓父布局?jǐn)r截事件料身,交給父布局處理汤纸。

                    歡迎關(guān)注我的微信公眾號,和我一起每天進步一點點芹血!
AntDream
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末贮泞,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子幔烛,更是在濱河造成了極大的恐慌啃擦,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,884評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饿悬,死亡現(xiàn)場離奇詭異令蛉,居然都是意外死亡,警方通過查閱死者的電腦和手機乡恕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,755評論 3 385
  • 文/潘曉璐 我一進店門言询,熙熙樓的掌柜王于貴愁眉苦臉地迎上來俯萎,“玉大人,你說我怎么就攤上這事运杭》虬。” “怎么了?”我有些...
    開封第一講書人閱讀 158,369評論 0 348
  • 文/不壞的土叔 我叫張陵辆憔,是天一觀的道長撇眯。 經(jīng)常有香客問我,道長虱咧,這世上最難降的妖魔是什么熊榛? 我笑而不...
    開封第一講書人閱讀 56,799評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮腕巡,結(jié)果婚禮上玄坦,老公的妹妹穿的比我還像新娘。我一直安慰自己绘沉,他們只是感情好煎楣,可當(dāng)我...
    茶點故事閱讀 65,910評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著车伞,像睡著了一般择懂。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上另玖,一...
    開封第一講書人閱讀 50,096評論 1 291
  • 那天困曙,我揣著相機與錄音,去河邊找鬼谦去。 笑死慷丽,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的哪轿。 我是一名探鬼主播盈魁,決...
    沈念sama閱讀 39,159評論 3 411
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼窃诉!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起赤套,我...
    開封第一講書人閱讀 37,917評論 0 268
  • 序言:老撾萬榮一對情侶失蹤飘痛,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后容握,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宣脉,經(jīng)...
    沈念sama閱讀 44,360評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,673評論 2 327
  • 正文 我和宋清朗相戀三年剔氏,在試婚紗的時候發(fā)現(xiàn)自己被綠了塑猖。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片竹祷。...
    茶點故事閱讀 38,814評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖羊苟,靈堂內(nèi)的尸體忽然破棺而出塑陵,到底是詐尸還是另有隱情,我是刑警寧澤蜡励,帶...
    沈念sama閱讀 34,509評論 4 334
  • 正文 年R本政府宣布令花,位于F島的核電站,受9級特大地震影響凉倚,放射性物質(zhì)發(fā)生泄漏兼都。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,156評論 3 317
  • 文/蒙蒙 一稽寒、第九天 我趴在偏房一處隱蔽的房頂上張望扮碧。 院中可真熱鬧,春花似錦杏糙、人聲如沸芬萍。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽柬祠。三九已至,卻和暖如春负芋,著一層夾襖步出監(jiān)牢的瞬間漫蛔,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,123評論 1 267
  • 我被黑心中介騙來泰國打工旧蛾, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留莽龟,地道東北人。 一個月前我還...
    沈念sama閱讀 46,641評論 2 362
  • 正文 我出身青樓锨天,卻偏偏與公主長得像毯盈,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子病袄,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,728評論 2 351

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