Android事件的分發(fā)和消費機制

事件的分發(fā)和消費機制

一、簡介:

Activity或View類的onTouchEvent()回調(diào)函數(shù)會接收到touch事件锨苏。

一個完整的手勢是從ACTION_DOWN開始,到ACTION_UP結(jié)束改橘。

簡單的情況下叠纹,我們只需要在onTouchEvent()中寫個switch case語句,處理各種事件(Touch Down嫌套、Touch Move局冰、Touch Up等),但是比較復(fù)雜的動作就需要更多的處理了灌危。

ViewGroup作為一個parent是可以截獲傳向它的child的touch事件的。

如果一個ViewGroup的onInterceptTouchEvent()方法返回true碳胳,說明Touch事件被截獲勇蝙,子View不再接收到Touch事件,而是轉(zhuǎn)向本ViewGroup的onTouchEvent()方法處理挨约。從Down開始味混,之后的Move产雹,Up都會直接在onTouchEvent()方法中處理。

先前還在處理touch event的child view將會接收到一個ACTION_CANCEL翁锡。

如果onInterceptTouchEvent()返回false蔓挖,則事件會交給child view處理。

Android中提供了ViewGroup馆衔、View瘟判、Activity三個層次的Touch事件處理。

處理過程是按照Touch事件從上到下傳遞角溃,再按照是否消費的返回值拷获,從下到上返回,即如果View的onTouchEvent返回false减细,將會向上傳給它的parent的ViewGroup匆瓜,如果ViewGroup不處理,將會一直向上返回到Activity未蝌。

即隧道式向下分發(fā)驮吱,然后冒泡式向上處理。

Android 中與 Touch 事件相關(guān)的方法包括:dispatchTouchEvent(MotionEvent ev)萧吠、onInterceptTouchEvent(MotionEvent ev)左冬、onTouchEvent(MotionEvent ev);能夠響應(yīng)這些方法的控件包括:ViewGroup怎憋、View又碌、Activity。方法與控件的對應(yīng)關(guān)系如下表所示:

Touch 事件相關(guān)方法方法功能ViewGroupViewActivity

public booleandispatchTouchEvent(MotionEvent ev)事件分發(fā)YesYesYes

public booleanonInterceptTouchEvent(MotionEvent ev)事件攔截YesYes / NoNo

public booleanonTouchEvent(MotionEvent ev)事件響應(yīng)YesYesYes

從這張表中我們可以看到 ViewGroup 和 View 對與 Touch 事件相關(guān)的三個方法均能響應(yīng)绊袋,而 Activity 對 onInterceptTouchEvent(MotionEvent ev) 也就是事件攔截不進行響應(yīng)毕匀。另外需要注意的是 View 對 onInterceptTouchEvent(MotionEvent ev) 的響應(yīng)的前提是可以向該 View 中添加子 View,如果當前的 View 已經(jīng)是一個最小的單元View(比如TextView)癌别,那么就無法向這個最小 View 中添加子 View皂岔,也就無法向子 View 進行事件的攔截,所以它沒有onInterceptTouchEvent(MotionEvent ev)展姐。

三個方法的用法:

dispatchTouchEvent()用來分派事件躁垛。

其中調(diào)用了onInterceptTouchEvent()和onTouchEvent(),一般不重寫該方法

onInterceptTouchEvent()用來攔截事件圾笨。

ViewGroup類中的源碼實現(xiàn)就是{return false;}表示不攔截該事件教馆,

事件將向下傳遞(傳遞給其子View);

若手動重寫該方法擂达,使其返回true則表示攔截土铺,事件將終止向下傳遞,

事件由當前ViewGroup類來處理,就是調(diào)用該類的onTouchEvent()方法

onTouchEvent()用來處理事件悲敷。

返回true則表示該View能處理該事件究恤,事件將終止向上傳遞(傳遞給其父View);

返回false表示不能處理后德,則把事件傳遞給其父View的onTouchEvent()方法來處理

【注】:ViewGroup的某些子類(GridView部宿、ScrollView...)重寫了onInterceptTouchEvent()方法,當發(fā)生ACTION_MOVE事件時瓢湃,返回true進行攔截理张。

一、Touch事件分析

(一)箱季、事件分發(fā):public booleandispatchTouchEvent(MotionEvent ev)

Touch事件發(fā)生時Activity的dispatchTouchEvent(MotionEvent ev)方法會以隧道方式(從根元素依次往下傳遞直到最內(nèi)層子元素或在中間某一元素中由于某一條件停止傳遞)將事件傳遞給最外層View的dispatchTouchEvent(MotionEvent ev)方法涯穷,并由該View的dispatchTouchEvent(MotionEvent ev)方法對事件進行分發(fā)。

(二)藏雏、事件攔截:public booleanonInterceptTouchEvent(MotionEvent ev)

在外層View的dispatchTouchEvent(MotionEvent ev)方法返回系統(tǒng)默認的super.dispatchTouchEvent(ev)情況下拷况,事件會自動的分發(fā)給當前View的onInterceptTouchEvent方法。onInterceptTouchEvent的事件攔截邏輯如下:

?如果onInterceptTouchEvent返回true掘殴,則表示將事件進行攔截赚瘦,并將攔截到的事件交由當前View的onTouchEvent進行處理;

?如果onInterceptTouchEvent返回false奏寨,則表示將事件放行起意,當前View上的事件會被傳遞到子View上,再由子View的dispatchTouchEvent來開始這個事件的分發(fā)病瞳;

?如果onInterceptTouchEvent返回super.onInterceptTouchEvent(ev)揽咕,事件默認不會被攔截,并將攔截到的事件交由當前View的onTouchEvent進行處理套菜。

(三)亲善、事件響應(yīng):public booleanonTouchEvent(MotionEvent ev)

在dispatchTouchEvent返回super.dispatchTouchEvent(ev)并且onInterceptTouchEvent返回true或返回super.onInterceptTouchEvent(ev)的情況下onTouchEvent會被調(diào)用。onTouchEvent的事件響應(yīng)邏輯如下:

?如果事件傳遞到當前View的onTouchEvent方法逗柴,而該方法返回了false蛹头,那么這個事件會從當前View向上傳遞,并且都是由上層View的onTouchEvent來接收戏溺,如果傳遞到上面的onTouchEvent也返回false渣蜗,這個事件就會“消失”,而且接收不到下一次事件旷祸。

?如果返回了true則會接收并消費該事件耕拷。

?如果返回super.onTouchEvent(ev)默認處理事件的邏輯和返回false時相同。

onInterceptTouchEvent用于改變事件的傳遞方向托享。決定傳遞方向的是返回值骚烧,返回為false時事件會傳遞給子控件控淡,返回值為true時事件會傳遞給當前控件的onTouchEvent(),這就是所謂的Intercept(攔截)止潘。

正確的使用方法是,在此方法內(nèi)僅判斷事件是否需要攔截辫诅,然后返回凭戴。即便需要攔截也應(yīng)該直接返回true,然后由onTouchEvent方法進行處理炕矮。

onTouchEvent用于處理事件么夫,返回值決定當前控件是否消費(consume)了這個事件。尤其對于ACTION_DOWN事件肤视,返回true档痪,表示我想要處理后續(xù)事件;返回false邢滑,表示不關(guān)心此事件腐螟,并返回由父類進行處理。

可能你要問是否消費了又區(qū)別嗎困后,反正我已經(jīng)針對事件編寫了處理代碼乐纸?答案是有區(qū)別!比如ACTION_MOVE或者ACTION_UP發(fā)生的前提是一定曾經(jīng)發(fā)生了ACTION_DOWN摇予,如果你沒有消費ACTION_DOWN汽绢,那么系統(tǒng)會認為ACTION_DOWN沒有發(fā)生過,所以ACTION_MOVE或者ACTION_UP就不能被捕獲侧戴。

在沒有重寫onInterceptTouchEvent()和onTouchEvent()的情況下(他們的返回值都是false)

Android系統(tǒng)中的每個View的子類都具有下面三個和TouchEvent處理密切相關(guān)的方法:

1)public boolean dispatchTouchEvent(MotionEvent ev)這個方法用來分發(fā)TouchEvent

2)public boolean onInterceptTouchEvent(MotionEvent ev)這個方法用來攔截TouchEvent

3)public boolean onTouchEvent(MotionEvent ev)這個方法用來處理TouchEvent

1宁昭、如果dispatchTouchEvent返回true,則交給這個view的onTouchEvent處理酗宋, 如果最終需要處理事件的view的onTouchEvent()返回了false积仗,那么該事件將被傳遞至其上一層次的view的onTouchEvent()處理。如果最終需要處理事件的view的onTouchEvent()返回了true本缠,那么后續(xù)事件將可以繼續(xù)傳遞給該view的onTouchEvent()處理斥扛。

2、如果dispatchTouchEvent6返回false丹锹,則交給這個view的interceptTouchEvent方法來決定是否

要攔截這個事件稀颁,如果interceptTouchEvent返回true,表示攔截掉了楣黍,則交給它的onTouchEvent來處理匾灶,如果interceptTouchEvent返回false,那么就傳遞給子view租漂,由子view的dispatchTouchEvent再來開始這個事件的分發(fā)阶女。

3颊糜、如果事件傳遞到某一層的子view的onTouchEvent上了,這個方法返回了false秃踩,那么這個事件

會從這個view往上傳遞衬鱼,都是onTouchEvent來接收。如果傳遞到最上面的onTouchEvent也返回false的話憔杨,這個事件就會“消失”鸟赫,而且接收不到下一次事件。

【備注:】

藍色區(qū)域為MyViewGroup消别、綠色為MyViewGroupInner抛蚤、紅色為MyView。分別繼承于LinearLayout寻狂、LinearLayout和TextView岁经。

1、核心代碼:

【以下動作均為點擊自定義View】

一蛇券、無攔截缀壤,無消費:12個

二、ViewGroup攔截怀读,無消費:7個

03-31 18:50:26.344: I/MainActivity(15304): --->dispatchTouchEvent: 0

03-31 18:50:26.344: I/MyViewGroup(15304): --->dispatchTouchEvent: 0

03-31 18:50:26.372: I/MyViewGroup(15304): --->onInterceptTouchEvent: 0

03-31 18:50:26.392: I/MyViewGroup(15304): --->onTouchEvent: 0

03-31 18:50:26.402: I/MainActivity(15304): --->onTouchEvent: 0

03-31 18:50:26.526: I/MainActivity(15304): --->dispatchTouchEvent: 1

03-31 18:50:26.526: I/MainActivity(15304): --->onTouchEvent: 1

三诉位、無攔截,View消費:14個

四菜枷、無攔截苍糠,僅有ViewGroup消費:12個

五、無攔截啤誊,Activity消費:12個

六岳瞭、無攔截、無消費蚊锹,僅在ViewGroup上設(shè)置單擊監(jiān)聽瞳筏,執(zhí)行單擊動作后:13個

七、無攔截牡昆、無消費姚炕,僅在ViewGroup上設(shè)置長按監(jiān)聽和單擊監(jiān)聽,執(zhí)行長按動作后:(如果長按返回false:14個 丢烘, 如果長按返回true:13個)

八柱宦、無攔截、無消費播瞳,僅僅在View上設(shè)置長按監(jiān)聽和單擊監(jiān)聽掸刊,執(zhí)行單擊動作后:15個

九、無攔截赢乓、無消費忧侧,在View上設(shè)置長按監(jiān)聽和單擊監(jiān)聽石窑,執(zhí)行長按動作后:(如果長按返回false:16個 , 如果長按返回true:15個)

十蚓炬、無攔截松逊、無消費,僅在View上設(shè)置clickable=true:14個

十一肯夏、無攔截棺棵、無消費,僅在ViewGroupInner上設(shè)置clickable=true:13個

A:dispatch:0

G:dispatch:0

G:intercept:0

Gi:dispatch:0

Gi:intercepte:0

V:dispatch:0

V:touch:0

Gi:touch:0

A:dispatch:1

G:disaptch:1

G:intercept:1

Gi:dispatch:1

Gi:touch:1

【備注:】

onLongClick按下到一定的時間就調(diào)用了,在ACTION_UP之前調(diào)用熄捍。如果長按返回false,則長按結(jié)束的ACTION_UP調(diào)用onClick(onClick是ACTION_UP之后調(diào)用的)母怜;如果長按返回true余耽,onLongClick后不再調(diào)用onClick。

Click事件處理

Click事件:View的短按和長按都是注冊監(jiān)聽器的(setListener):

onClick是在ACTION_UP之后執(zhí)行的苹熏。

onLongClick則是按下到一定時間之后執(zhí)行的碟贾,這個時間是ViewConfiguration中的:

private static final int TAP_TIMEOUT =180;//180毫秒

這里需要注意onLongClick的返回值,如果是false轨域,則onLongClick之后袱耽,手指抬起,ACTION_UP之后還是會執(zhí)行到onClick干发;但是如果onLongClick返回true朱巨,則不會再調(diào)用onClick。

【備注:】

如果一個View是Clickable或者longClickable枉长,onTouchEvent()就直接返回true,表示該View就一直消費Touch事件冀续。也就是說,一個clickable或者longclickable的View是一直消費Touch事件的必峰,而一般的View既不是clickable也不是longclickable的(即不會消費Touch事件洪唐,只會執(zhí)行ACTION_DOWN而不會執(zhí)行ACTION_MOVE和ACTION_UP)。

Button是clickable的吼蚁,可以消費Touch事件凭需,但是我們可以通過setClickable()和setLongClickable()來設(shè)置View是否為clickable和longClickable。當然還可以通過重寫View的onTouchEvent()方法來控制Touch事件的消費與否肝匆。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末粒蜈,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子术唬,更是在濱河造成了極大的恐慌薪伏,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,126評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件粗仓,死亡現(xiàn)場離奇詭異嫁怀,居然都是意外死亡设捐,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,254評論 2 382
  • 文/潘曉璐 我一進店門塘淑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來萝招,“玉大人,你說我怎么就攤上這事存捺』闭樱” “怎么了?”我有些...
    開封第一講書人閱讀 152,445評論 0 341
  • 文/不壞的土叔 我叫張陵捌治,是天一觀的道長岗钩。 經(jīng)常有香客問我,道長肖油,這世上最難降的妖魔是什么兼吓? 我笑而不...
    開封第一講書人閱讀 55,185評論 1 278
  • 正文 為了忘掉前任,我火速辦了婚禮森枪,結(jié)果婚禮上视搏,老公的妹妹穿的比我還像新娘。我一直安慰自己县袱,他們只是感情好浑娜,可當我...
    茶點故事閱讀 64,178評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著式散,像睡著了一般筋遭。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上暴拄,一...
    開封第一講書人閱讀 48,970評論 1 284
  • 那天宛畦,我揣著相機與錄音,去河邊找鬼揍移。 笑死次和,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的那伐。 我是一名探鬼主播踏施,決...
    沈念sama閱讀 38,276評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼罕邀!你這毒婦竟也來了畅形?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,927評論 0 259
  • 序言:老撾萬榮一對情侶失蹤诉探,失蹤者是張志新(化名)和其女友劉穎日熬,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肾胯,經(jīng)...
    沈念sama閱讀 43,400評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡竖席,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,883評論 2 323
  • 正文 我和宋清朗相戀三年耘纱,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片毕荐。...
    茶點故事閱讀 37,997評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡束析,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出憎亚,到底是詐尸還是另有隱情员寇,我是刑警寧澤,帶...
    沈念sama閱讀 33,646評論 4 322
  • 正文 年R本政府宣布第美,位于F島的核電站蝶锋,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏什往。R本人自食惡果不足惜牲览,卻給世界環(huán)境...
    茶點故事閱讀 39,213評論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望恶守。 院中可真熱鬧,春花似錦贡必、人聲如沸兔港。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,204評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽衫樊。三九已至,卻和暖如春利花,著一層夾襖步出監(jiān)牢的瞬間科侈,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,423評論 1 260
  • 我被黑心中介騙來泰國打工炒事, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留臀栈,地道東北人。 一個月前我還...
    沈念sama閱讀 45,423評論 2 352
  • 正文 我出身青樓挠乳,卻偏偏與公主長得像权薯,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子睡扬,可洞房花燭夜當晚...
    茶點故事閱讀 42,722評論 2 345

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