關(guān)于Android事件機(jī)制亏镰,很多大神都做了分析嬉探,我也是通過他們的帖子進(jìn)行學(xué)習(xí)臼寄,在此特向他們表達(dá)謝意伪朽。
Android事件機(jī)制(一)從代碼演示的角度整理了一下我對Android事件傳遞的理解,下面就通過源碼淺顯的分析一下個(gè)人理解(想想源碼铭乾,就覺任重而道遠(yuǎn)剪廉,笑cry)。
那么來看一下上篇中提到的炕檩,“有觸摸斗蒋,就有dispatchTouchEvent
方法的調(diào)用”,這個(gè)dispatchTouchEvent
方法笛质。
雖然dispatchTouchEvent
方法是事件分發(fā)的第一步泉沾,但是一般情況下不太會(huì)去改寫這個(gè)方法,只是onInterceptTouchEvent妇押、onTouchEvent跷究、OnTouchListener.onTouch
這幾個(gè)方法都會(huì)被其以不同的條件調(diào)用,這也就決定了事件如何傳遞敲霍。
點(diǎn)擊MyView俊马,首先會(huì)調(diào)用到MyView最高父輩的dispatchTouchEvent
方法,這里從MyViewGroupA.dispatchTouchEvent
開始色冀,而MyViewGroupA繼承自RelativeLayout潭袱,因此我們進(jìn)入到了ViewGroup.dispatchTouchEvent
。
當(dāng)事件到來的時(shí)候锋恬,ViewGroup.dispatchTouchEvent
會(huì)先判斷是否要攔截事件:
從2094行可以看出屯换,事件機(jī)制從MotionEvent.ACTION_DOWN
開始,此時(shí)會(huì)重置所有狀態(tài)与学。
從2014行開始彤悔,對是否攔截的boolean變量進(jìn)行判斷賦值。
如果不攔截:
從2164-2221行就會(huì)對子View進(jìn)行遍歷索守,找到處于點(diǎn)擊范圍的合適的子View晕窑,找到之后,通過2197行的dispatchTransformedTouchEvent
方法來調(diào)用child的dispatchTouchEvent
方法卵佛,并獲取它的返回值杨赤,根據(jù)返回的值來判斷子View是否成功消耗了事件,如果返回的是true代表成功消費(fèi)截汪,那么就會(huì)對mFirstTouchTarget進(jìn)行賦值疾牲,從而不會(huì)進(jìn)入2238行的條件判斷,不會(huì)調(diào)用super的dispatchTouchEvent
方法衙解,事件也就停止了傳遞:
如果child沒有消耗事件阳柔,即child的dispatchTouchEvent
方法返回了false
,或者沒有發(fā)現(xiàn)合適的子View蚓峦,即child == null
舌剂,那么就不會(huì)給mFirstTouchTarget進(jìn)行賦值济锄,即mFirstTouchTarget == null
,通過dispatchTransformedTouchEvent
方法就會(huì)調(diào)用到super的dispatchTouchEvent
方法霍转。
如果進(jìn)行了攔截荐绝,那么直接通過2238行進(jìn)而執(zhí)行2240行,調(diào)用super.dispatchTouchEvent
方法谴忧,自己處理事件很泊。
dispatchTransformedTouchEvent
的部分源碼如下:
經(jīng)過上述分析,可以知道:
如果不設(shè)置攔截沾谓,MyViewGroupA的直屬child是MyViewGroupB委造,因此MyViewGroupA就會(huì)調(diào)用到MyViewGroupB的dispatchTouchEvent
方法,而MyViewGroupB的直屬child是MyView均驶,所以就會(huì)調(diào)用到View的dispatchTouchEvent
方法昏兆。
假設(shè)MyViewGroupA或者M(jìn)yViewGroupB設(shè)置了事件攔截,那么就會(huì)調(diào)用ViewGroup的super.dispatchTouchEvent
妇穴,而ViewGroup繼承自View爬虱,所以最終還是調(diào)用了View的dispatchTouchEvent
方法。
那么接下來就看一下View.dispatchTouchEvent
方法:
可見腾它,上面說的onTouchEvent跑筝、OnTouchListener.onTouch
會(huì)被dispatchTouchEvent
調(diào)用,不是瞎掰的瞒滴。
通過黃框圈中的代碼可以很明顯的看出View.dispatchTouchEvent
的返回值受onTouchEvent曲梗、OnTouchListener.onTouch
影響,并且OnTouchListener.onTouch
優(yōu)先于onTouchEvent
妓忍。
源碼就先看到這里虏两,來捋一下上篇中Log_1所示的流程執(zhí)行:
點(diǎn)擊MyView,觸發(fā)ACTION_DOWN
:
(1) 首先調(diào)用MyViewGroupA.dispatchTouchEvent
世剖,在MyViewGroupA.dispatchTouchEvent
中調(diào)用MyViewGroupA.onInterceptTouchEvent
定罢,返回false
,不攔截事件旁瘫;
(2) 找到MyViewGroupA的子View祖凫,即MyViewGroupB,調(diào)用MyViewGroupB.dispatchTouchEvent
酬凳,MyViewGroupB.dispatchTouchEvent
在執(zhí)行中調(diào)用MyViewGroupB.onInterceptTouchEvent
蝙场,返回false
,不攔截事件粱年;
(3) 找到MyViewGroupB的子View,即MyView罚拟,調(diào)用MyView.dispatchTouchEvent
台诗,由于沒有設(shè)置OnTouchListener完箩,因此在MyView.dispatchTouchEvent
中就會(huì)調(diào)用MyView.onTouchEvent
,而MyView.onTouchEvent
這個(gè)方法返回false
拉队,因此就使MyView.dispatchTouchEvent
返回false
弊知,也就是說,MyView沒有消費(fèi)這個(gè)事件粱快;
(4) 接著秩彤,MyViewGroupB.dispatchTouchEvent
中接收到MyView返回的結(jié)果,發(fā)現(xiàn)MyView沒有消費(fèi)事件事哭,mFirstTouchTarget就不會(huì)被賦值漫雷,即mFirstTouchTarget == null
,那么根據(jù)ViewGroup.dispatchTouchEvent
源碼的第2238行鳍咱,就會(huì)去調(diào)用super.dispatchTouchEvent
降盹,進(jìn)而調(diào)用到MyViewGroupB.onTouchEvent
這個(gè)方法,而這個(gè)方法返回false
谤辜,因此就使MyViewGroupB.dispatchTouchEvent
返回false
蓄坏,也就是說,MyViewGroupB也沒有消費(fèi)這個(gè)事件丑念;
(5) 最后涡戳,MyViewGroupA.dispatchTouchEvent
中接收到MyViewGroupB返回的結(jié)果,發(fā)現(xiàn)MyViewGroupB沒有消費(fèi)事件脯倚,mFirstTouchTarget就不會(huì)被賦值渔彰,即mFirstTouchTarget == null
,那么根據(jù)ViewGroup.dispatchTouchEvent
源碼的第2238行挠将,就會(huì)去調(diào)用super.dispatchTouchEvent
胳岂,進(jìn)而調(diào)用到MyViewGroupA.onTouchEvent
這個(gè)方法,自己處理和消費(fèi)事件舔稀。
(注:這個(gè)過程畫圖應(yīng)該會(huì)更清晰乳丰,這里我是按照自己的語言分析順序?qū)懙模哪┑膮⒖兼溄又杏袌D内贮,而且很清晰)
在事件序列中产园,總是以ACTION_DOWN
開始,如果不是ACTION_DOWN
夜郁,說明事件序列已經(jīng)開始傳遞了什燕。如果對ACTION_DOWN
不消費(fèi),那么就可以理解為竞端,我不要這個(gè)事件屎即,也就沒有了接下來ACTION_MOVE、ACTION_UP
的傳遞和處理。
這一篇就先分析到這里技俐,太長了看著會(huì)厭煩乘陪,下一篇分析一下onTouchEvent
返回true
的情況。
參考:
Android事件分發(fā)機(jī)制完全解析雕擂,帶你從源碼的角度徹底理解(上)
Android事件分發(fā)機(jī)制完全解析啡邑,帶你從源碼的角度徹底理解(下)
Android事件分發(fā)機(jī)制詳解
Android群英傳-事件攔截機(jī)制分析