事件的處理對象們
Android中View的事件處理用的是設(shè)計(jì)模式中的職責(zé)鏈模式。整個(gè)職責(zé)鏈中的處理對象是這樣的:Activity->ViewGroup->View澈段。
事件處理的三個(gè)重要階段
這三類事件處理對象對事件的處理主要有三個(gè)階段省有,對應(yīng)三個(gè)重要的方法:
- 事件分發(fā):
boolean dispatchTouchEvent(MotionEvent ev)
讓當(dāng)前處理對象決定是由自己來消費(fèi)事件英支,還是將事件交給子View來處理碉哑。Activity度陆、ViewGroup蔽介、View都有這個(gè)方法摘投。 - 事件攔截:
boolean onInterceptTouchEvent(MotionEvent ev)
讓處理對象決定要不要繼續(xù)把時(shí)間傳遞給子View,還是自己消費(fèi)掉算了 虹蓄。只有ViewGroup有這個(gè)方法犀呼。 - 事件消費(fèi):
boolean onTouchEvent(MotionEvent ev)
決定是否消費(fèi)掉事件,如何消費(fèi)薇组。Activity外臂、ViewGroup、View都有這個(gè)方法律胀。
這三個(gè)方法不同的返回值會影響事件的傳遞宋光,其實(shí)還是挺復(fù)雜的。今天趁著勞動節(jié)累铅,我來做做體力勞動跃须,自定義一個(gè)ViewGroup和一個(gè)View,通過日志打印出這三個(gè)方法在不同的返回值下的調(diào)用娃兽。
onTouchEvent
all onTouchEvent false
viewGroup onTouchEvent true, view onTouchEvent false
- 返回
false
或者是super.onTouchEvent
時(shí)菇民,表示當(dāng)前View不想消費(fèi)事件。則會逐級調(diào)用其父控件的onTouchEvent
方法投储,直到調(diào)用的父控件的onTouchEvent
返回true為止第练,即直到有父控件消費(fèi)掉事件為止,或者是最終被Activity消費(fèi)掉事件玛荞。這就是典型的職責(zé)鏈模式 - 當(dāng)某個(gè)事件在最終被某個(gè)對象的
onTouchEvent
消費(fèi)掉后娇掏,這個(gè)事件之后連續(xù)的事件都會在分發(fā)到那個(gè)對象后,直接被它的onTouchEvent
消費(fèi)掉勋眯,而不會繼續(xù)傳遞給子View了婴梧。從日志當(dāng)中可以看出下梢,View和ViewGroup的onTouchEvent
都返回false
,不消費(fèi)事件塞蹭,ACTION_DOWN事件最終被Activity消費(fèi)掉孽江。這此后ACTION_UP在被dispatch到Activity之后,就直接調(diào)用Activity的onTouchEvent
番电,不會繼續(xù)往下傳遞給子View了岗屏。同樣的,當(dāng)ViewGroup消費(fèi)ACTION_DOWN事件后漱办,接下來的ACTION_MOVE, ACTION_UP都會在dispatch到ViewGroup后这刷,調(diào)用ViewGroup的onTouchEvent
。
onTouchEvent true
-
onTouchEvent
返回true
時(shí)娩井,表示當(dāng)前View想要消費(fèi)掉事件暇屋。連續(xù)的所有的事件都會逐層被分發(fā)到當(dāng)前View后,調(diào)用onTouchEvent
方法
onInterceptTouchEvent
ViewGroup onInterceptTouchEvent=true, onTouchEvent=true
ViewGroup onInterceptTouchEvent=true, onTouchEvent=false
-
onInterceptTouchEvent
返回true
時(shí)洞辣,表示當(dāng)前ViewGroup想攔截事件率碾。此時(shí)會調(diào)用ViewGroup的onTouchEvent
方法,它的子View會收到ACTION_CANCEL事件屋彪。如果ViewGroup的onTouchEvent
返回true
,則接下來的事件都會直接交給ViewGroup的onTouchEvent
去處理绒尊,不會再調(diào)用onInterceptTouchEvent
了畜挥。也就是ViewGroup從某個(gè)點(diǎn)攔截住事件,并且消費(fèi)掉事件后婴谱,就可以直接處理接下來的事件而不需要再次攔截了蟹但。如果ViewGroup的onTouchEvent
返回false
,那么事件會被逐級向上傳給它的父View的onTouchEvent
去處理谭羔,并且此后連續(xù)的事件都不會傳遞給當(dāng)前的ViewGroup了华糖,也就是說當(dāng)前ViewGroup再也沒有機(jī)會收到接下來的事件了。因此瘟裸,一般在自定義ViewGroup時(shí)客叉,onInterceptTouchEvent
返回true
開始攔截事件時(shí),都要讓onTouchEvent
返回true
话告,并在onTouchEvent
處理接下來的事件兼搏。
ViewGroup onInterceptTouchEvent=false
- 當(dāng)
onInterceptTouchEvent
返回false
或者super.onInterceptTouchEvent
時(shí),表示當(dāng)前ViewGroup不攔截事件沙郭,事件分發(fā)到子View - 當(dāng)還沒有確定事件講會被哪個(gè)View處理時(shí)佛呻,事件在分發(fā)階段都會調(diào)用
onInterceptTouchEvent
,但是一旦事件的處理對象明確后病线,onInterceptTouchEvent
講不會再被調(diào)用吓著。這就是為什么鲤嫡,當(dāng)最底層的ViewonTouchEvent
返回true
消費(fèi)掉事件后,接下來的事件不知道到底會被誰處理绑莺,因此被下發(fā)到底層View的過程中還是會調(diào)用ViewGroup的onInterceptTouchEvent
暖眼。而當(dāng)事件被中途攔截,或者會被上層的某個(gè)父View處理后紊撕,ViewGroup的onInterceptTouchEvent
都不會被調(diào)用罢荡。
dispatchTouchEvent
Paste_Image.png
- 返回'true',事件無疾而終对扶,接下來的連續(xù)的事件也無疾而終無人處理区赵。
Paste_Image.png
- 返回
false
,事件也不會繼續(xù)往下傳遞浪南,但是會被逐級向上傳遞給父view的onTouchEvent
去處理笼才。 - 返回
super.dispatchTouchEvent
時(shí),事件才會正常的往下傳遞給子View络凿。一開始學(xué)習(xí)的時(shí)候骡送,我有一個(gè)誤區(qū),認(rèn)為這三個(gè)方法要么返回true絮记,要么返回false摔踱,返回true的時(shí)候是自己處理事件,返回false的時(shí)候是讓子view去處理事件怨愤。其實(shí)不是這樣的派敷,返回true的時(shí)候確實(shí)是讓自己來處理事件,但是必須要調(diào)用super.dispatchTouchEvent
才會把事件傳遞給子View進(jìn)行處理撰洗。 - 參考簡書其它朋友寫的文章:
ViewGroup的dispatchTouchEvent是真正在執(zhí)行“分發(fā)”工作篮愉,而View的dispatchTouchEvent方法,并不執(zhí)行分發(fā)工作差导,或者說它分發(fā)的對象就是自己试躏。一般情況下,我們不該在普通View內(nèi)重寫dispatchTouchEvent方法设褐,因?yàn)樗⒉粓?zhí)行分發(fā)邏輯颠蕴。當(dāng)Touch事件到達(dá)View時(shí),我們該做的就是是否在onTouchEvent事件中處理它助析。