Android觸摸滑動(dòng)全解(二)——ViewGroup中觸摸事件詳解
VieGroup觸摸事件概述
ViewGroup繼承自View介牙,很多方法都和View中一樣,不同的是它重載了dispatchTouchEvent()
方法,并且它多了一個(gè)自身的方法onInterceptTouchEvent()
削饵。
ViewGroup觸摸事件調(diào)用原則
dispatchTouchEvent()
中,會(huì)對(duì)子View進(jìn)行遞歸,將dispatchTouchEvent()
傳遞給它的每一個(gè)子View锣枝,如果某一個(gè)子View在ACTION_DOWN
的時(shí)候沒(méi)有消費(fèi)或者攔截事件,那么這個(gè)子View后續(xù)也將無(wú)法收到ACTIION_MOVE
和ACTION_UP
等事件兰英。ViewGroup可以在
onInterceptTouchEvent()
中攔截事件撇叁,onInterceptTouchEvent()
在之后執(zhí)行,如果返回true
畦贸,則會(huì)執(zhí)行自身的onTouchEvent()
方法陨闹,但后續(xù)onInterceptTouchEvent()
和onTouchEvent()
將不會(huì)調(diào)用楞捂。ViewGroup如果
onInterceptTouchEvent()
返回false
,那么ViewGroup后續(xù)是否繼續(xù)調(diào)用dispatchTouchEvent()
取決于子View是否消費(fèi)觸摸事件趋厉。ViewGroup如果返回在
dispatchTouchEvent()
中返回false
寨闹,那么后續(xù)所有的觸摸操作都不會(huì)再執(zhí)行!ViewGroup在
dispatchTouchEvent()
中return true
和return super.dispatchTouchEvent(ev)
會(huì)導(dǎo)致觸摸結(jié)果不一樣君账!
ViewGroup攔截觸摸事件示例
自定義一個(gè)ViewGroup繼承LinearLayout繁堡,展示ViewGroup對(duì)觸摸事件的攔截。子View默認(rèn)消費(fèi)此事件乡数。
1椭蹄、正常狀態(tài)(子View消費(fèi)觸摸事件)
此時(shí):
ViewGroup:dispatchTouchEvent()
為super.dispatchTouchEvent(ev)
,onInterceptTouchEvent()
為false
净赴,OnTouchEvent()
為false
绳矩。
子View:dispatchTouchEvent()
返回true
,OnTouchEvent()
返回true
劫侧。
Log打勇癯辍:
zw: LinearLayout dispatchTouchEvent down
LinearLayout onInterceptTouchEvent down
View dispatchTouchEvent down
View onTouchEvent down
zw: LinearLayout dispatchTouchEvent move
LinearLayout onInterceptTouchEvent move
View dispatchTouchEvent move
View onTouchEvent move
zw: LinearLayout dispatchTouchEvent up
LinearLayout onInterceptTouchEvent up
View dispatchTouchEvent up
View onTouchEvent up
可以看見(jiàn),子View將事件消費(fèi)掉了烧栋,因此ViewGroup沒(méi)有調(diào)用OnTouchEvent()
方法写妥。
調(diào)用順序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN)
> ViewGroup.onInterceptTouchEvent(DOWN)
> View.dispatchTouchEvent(DOWN)
> View.OnTouchEvent(DOWN)
>
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE)
> ViewGroup.onInterceptTouchEvent(MOVE)
> View.dispatchTouchEvent(MOVE)
> View.OnTouchEvent(MOVE)
>
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP)
> ViewGroup.onInterceptTouchEvent(UP)
> View.dispatchTouchEvent(UP)
> View.OnTouchEvent(UP)
> 結(jié)束
2、正常狀態(tài)(子View不消費(fèi)觸摸事件)
此時(shí):
ViewGroup:dispatchTouchEvent()
為super.dispatchTouchEvent(ev)
审姓,onInterceptTouchEvent()
為false
珍特,OnTouchEvent()
為false
。
子View:dispatchTouchEvent()
返回false
魔吐,OnTouchEvent()
返回false
扎筒。
Log打印:
zw: LinearLayout dispatchTouchEvent down
LinearLayout onInterceptTouchEvent down
View dispatchTouchEvent down
View onTouchEvent down
LinearLayout onTouchEvent down
可以看見(jiàn)酬姆,子View沒(méi)有將事件消費(fèi)掉嗜桌,因此ViewGroup調(diào)用OnTouchEvent()
方法。
調(diào)用順序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN)
> ViewGroup.onInterceptTouchEvent(DOWN)
> View.dispatchTouchEvent(DOWN)
> View.OnTouchEvent(DOWN)
> ViewGroup.onTouchEvent(DOWN)
> 結(jié)束
3辞色、ViewGroup攔截但不消費(fèi)觸摸事件(子View消費(fèi)觸摸事件)
此時(shí):
ViewGroup:dispatchTouchEvent()
為super.dispatchTouchEvent(ev)
骨宠,onInterceptTouchEvent()
為true
,OnTouchEvent()
為false
相满。
子View:dispatchTouchEvent()
返回true
层亿,OnTouchEvent()
返回true
。
Log打恿⒚馈:
zw: LinearLayout dispatchTouchEvent down
LinearLayout onInterceptTouchEvent down
LinearLayout onTouchEvent down
可以看見(jiàn)匿又,子View沒(méi)有將事件消費(fèi)掉,因?yàn)閂iewGroup的onInterceptTouchEvent()
返回了true
建蹄,攔截了此次事件碌更,但是它本身也沒(méi)有消費(fèi)裕偿,因此,ACTION_DOWN后面沒(méi)有再繼續(xù)執(zhí)行了针贬。
調(diào)用順序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN)
> ViewGroup.onInterceptTouchEvent(DOWN)
> ViewGroup.onTouchEvent(DOWN)
> 結(jié)束
4击费、ViewGroup攔截并消費(fèi)觸摸事件(子View消費(fèi)觸摸事件)
此時(shí):
ViewGroup:dispatchTouchEvent()
為super.dispatchTouchEvent(ev)
,onInterceptTouchEvent()
為true
桦他,OnTouchEvent()
為true
。
子View:dispatchTouchEvent()
返回true
谆棱,OnTouchEvent()
返回true
快压。
Log打印:
zw: LinearLayout dispatchTouchEvent down
LinearLayout onInterceptTouchEvent down
LinearLayout onTouchEvent down
zw: LinearLayout dispatchTouchEvent move
LinearLayout onTouchEvent move
zw: LinearLayout dispatchTouchEvent up
LinearLayout onTouchEvent up
可以看見(jiàn)垃瞧,子View沒(méi)有將事件消費(fèi)掉蔫劣,因?yàn)閂iewGroup的onInterceptTouchEvent()
返回了true
,攔截了此次事件个从,但是它本身消費(fèi)了此次事件脉幢,因此,只有ViewGroup自身在消費(fèi)本次觸摸事件嗦锐。
調(diào)用順序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN)
> ViewGroup.onInterceptTouchEvent(DOWN)
> ViewGroup.onTouchEvent(DOWN)
>
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE)
> ViewGroup.onTouchEvent(MOVE)
>
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP)
> ViewGroup.onTouchEvent(UP)
>
5嫌松、ViewGroup沒(méi)攔截但消費(fèi)觸摸事件(子View消費(fèi)觸摸事件)
此時(shí):
ViewGroup:dispatchTouchEvent()
為super.dispatchTouchEvent(ev)
,onInterceptTouchEvent()
為false
奕污,OnTouchEvent()
為true
萎羔。
子View:dispatchTouchEvent()
返回true
,OnTouchEvent()
返回true
碳默。
Log打蛹窒荨:
zw: LinearLayout dispatchTouchEvent down
LinearLayout onInterceptTouchEvent down
View dispatchTouchEvent down
View onTouchEvent down
zw: LinearLayout dispatchTouchEvent move
LinearLayout onInterceptTouchEvent move
View dispatchTouchEvent move
View onTouchEvent move
zw: LinearLayout dispatchTouchEvent up
LinearLayout onInterceptTouchEvent up
View dispatchTouchEvent up
View onTouchEvent up
可以看見(jiàn),如果ViewGroup的onInterceptTouchEvent()
沒(méi)有返回true
而dispatchTouchEvent()
返回true
嘱根,那么即使OnTouchEvent()
返回的是true
髓废,也不會(huì)調(diào)用OnTouchEvent()
方法,需要根據(jù)子View的消費(fèi)情況來(lái)判斷该抒!
調(diào)用順序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN)
> ViewGroup.onInterceptTouchEvent(DOWN)
> View.dispatchTouchEvent(DOWN)
> View.onTouchEvent(DOWN)
>
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE)
> ViewGroup.onInterceptTouchEvent(MOVE)
> View.dispatchTouchEvent(MOVE)
> View.onTouchEvent(MOVE)
>
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP)
>ViewGroup.onInterceptTouchEvent(UP)
> View.dispatchTouchEvent(UP)
> View.onTouchEvent(UP)
>
子View強(qiáng)制消費(fèi)觸摸事件的方法
當(dāng)ViewGroup在onInterceptTouchEvent()
返回true
消費(fèi)事件后慌洪,子View則無(wú)法消費(fèi)此觸摸事件,此時(shí)柔逼,子View如果一定要消費(fèi)此觸摸事件的話蒋譬,可以使用getParent().requestDisallowInterceptTouchEvent(true)
來(lái)讓ViewGroup不攔截本次事件。
但是此方法需要滿足一個(gè)條件愉适,就是ViewGroup在onInterceptTouchEvent()
的ACTION_DOWN時(shí)不要攔截犯助,如果攔截了,那么此方法失效维咸。
正常情況
此時(shí):
ViewGroup:dispatchTouchEvent()
為true
剂买,onInterceptTouchEvent()
在ACITON_DOWN時(shí)為false
惠爽,其余返回true
,OnTouchEvent()
為true
瞬哼。
子View:dispatchTouchEvent()
返回true
婚肆,OnTouchEvent()
返回true
。
Log打幼俊:
zw: LinearLayout dispatchTouchEvent down
LinearLayout onInterceptTouchEvent down
View dispatchTouchEvent down
View onTouchEvent down
zw: LinearLayout dispatchTouchEvent move
LinearLayout onInterceptTouchEvent move
LinearLayout dispatchTouchEvent move
LinearLayout onTouchEvent move
zw: LinearLayout dispatchTouchEvent up
LinearLayout onTouchEvent up
可以看見(jiàn)较性,子View只有dispatchTouchEvent(DOWN)
和onTouchEvent(DOWN)
被調(diào)用了,其余的都被攔截了结胀。(這就是ViewGroup的onInterceptTouchEvent()
必須在ACTION_DOWN時(shí)返回false
的原因赞咙,不然子View根本沒(méi)有調(diào)用的機(jī)會(huì))
調(diào)用順序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN)
> ViewGroup.onInterceptTouchEvent(DOWN)
> View.dispatchTouchEvent(DOWN)
> View.onTouchEvent(DOWN)
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE)
> ViewGroup.onInterceptTouchEvent(MOVE)
> ViewGroup.dispatchTouchEvent(MOVE)
> ViewGroup.onTouchEvent(MOVE)
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP)
> ViewGroup.onTouchEvent(UP)
> 結(jié)束
子View調(diào)用代碼的情況
基本和上面一樣,只是子View在onTouchEvent()
的ACTION_DOWN中調(diào)用代碼:
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
getParent().requestDisallowInterceptTouchEvent(true);
Log.e("zw","onTouchEvent down");
break;
case MotionEvent.ACTION_MOVE:
Log.e("zw","onTouchEvent move");
break;
case MotionEvent.ACTION_UP:
Log.e("zw","onTouchEvent up");
break;
}
return true;
}
Log打釉愀邸:
zw: LinearLayout dispatchTouchEvent down
LinearLayout onInterceptTouchEvent down
View dispatchTouchEvent down
View onTouchEvent down
zw: LinearLayout dispatchTouchEvent move
View dispatchTouchEvent move
View onTouchEvent move
zw: LinearLayout dispatchTouchEvent up
View dispatchTouchEvent up
View onTouchEvent up
可以看見(jiàn)攀操,本次觸摸事件被子View強(qiáng)制消費(fèi)掉了。
調(diào)用順序:
ACTION_DOWN:
ViewGroup.dispatchTouchEvent(DOWN)
> ViewGroup.onInterceptTouchEvent(DOWN)
> View.dispatchTouchEvent(DOWN)
> View.onTouchEvent(DOWN)
ACTION_MOVE:
ViewGroup.dispatchTouchEvent(MOVE)
> View.dispatchTouchEvent(MOVE)
> View.onTouchEvent(MOVE)
ACTION_UP:
ViewGroup.dispatchTouchEvent(UP)
> View.dispatchTouchEvent(UP)
>View.onTouchEvent(UP)
> 結(jié)束
總結(jié)
1秸抚、如果ViewGroup要消費(fèi)觸摸事件的話速和,一定要在onInterceptTouchEvent()
返回true
,否則它是否調(diào)用onTouchEvent()
方法取決于子View是否消費(fèi)此事件剥汤!
2颠放、ViewGroup的dispatchTouchEvent()
中盡量不要直接返回true
或false
,因?yàn)檫@樣導(dǎo)致ViewGroup不會(huì)再去分發(fā)任何事件秀姐!最好返回super.dispatchTouchEvent(ev)
慈迈!
3、子View可以通過(guò)調(diào)用getParent().requestDisallowInterceptTouchEvent(true)
方法來(lái)強(qiáng)制消費(fèi)事件(有限制條件)省有。