事件分發(fā)機(jī)制大家應(yīng)該都熟記于心瑟俭,默認(rèn)事件分發(fā)是逆序的谒臼,有哪些方法可以修改分發(fā)順序价匠?
修改事件分發(fā)順序的話墨微,在日常開(kāi)發(fā)中基本遇不到,因?yàn)楝F(xiàn)在的逆序遍歷陪捷,是跟View的層級(jí)顯示相匹配的回窘,隨便更改反而不太合理。
如果非要修改這個(gè)順序市袖,很多同學(xué)首先會(huì)想到:重寫dispatchTouchEvent()方法啡直,然后在里面一個(gè)for循環(huán),從0開(kāi)始一個(gè)個(gè)調(diào)用子View的dispatchTouchEvent()苍碟。
這個(gè)方法酒觅,不是說(shuō)絕對(duì)不行,只是你要做的事情很多微峰,就比如觸摸坐標(biāo)的轉(zhuǎn)換
我們都知道舷丹,View Group在分派事件的時(shí)候,會(huì)檢查子View是否應(yīng)用過(guò)屬性動(dòng)畫(huà)的(位移蜓肆、縮放颜凯、旋轉(zhuǎn)等)谋币,如果有的話還要把坐標(biāo)給映射回去。接著症概,還會(huì)把相對(duì)于這個(gè)View Group本身的觸摸坐標(biāo) 轉(zhuǎn)換成 相對(duì)于對(duì)應(yīng)子View的觸摸坐標(biāo)蕾额。
這樣說(shuō)可能有點(diǎn)繞,
舉個(gè)例子彼城,比如:當(dāng)手指在屏幕中按下凡简,ViewGroup中收到的event坐標(biāo)(getX,getY)假設(shè)是【500,500】,剛好在這個(gè)位置上有個(gè)子View精肃,那接下來(lái)肯定會(huì)把事件傳給這個(gè)子View的dispatchTouchEvent,這時(shí)候如果坐標(biāo)不轉(zhuǎn)換直接傳的話帜乞,那子View收到的event坐標(biāo)(getX,getY)也是【500,500】司抱,這明顯是不對(duì)的,正確的坐標(biāo)應(yīng)該要分別減去它的left和top黎烈。這看起來(lái)好像沒(méi)什么大的影響习柠,但如果你的子View沒(méi)有重寫onTouchEvent方法的話(比如子View是常用的ImageView,TextView之類的)照棋,你的OnClickListener就會(huì)無(wú)效了资溃,因?yàn)槟J(rèn)的onTouchEvent在處理ACTION_MOVE的時(shí)候,會(huì)檢查event的坐標(biāo)是否已經(jīng)脫離了View的邊界范圍烈炭,如果在邊界范圍之外的話溶锭,pressed將會(huì)失效(認(rèn)為沒(méi)有被按下),當(dāng)ACTION_UP時(shí)符隙,如果pressed為false趴捅,就不會(huì)執(zhí)行PerformClick。
那難道沒(méi)有方法可以完美地做到了嗎霹疫?
在ViewGroup的dispatchTouchEvent方法中拱绑,雖然它是逆序的for,但是呢丽蝎,它把子View拿出來(lái)的時(shí)候猎拨,卻不是直接操作的mChildren數(shù)組,而是通過(guò)一個(gè)getAndVerifyPreorderedView方法來(lái)獲得屠阻,這個(gè)方法會(huì)把當(dāng)前索引傳進(jìn)去红省,還有一個(gè)preorderedList。如果傳進(jìn)去的preorderedList不為空栏笆,那么就會(huì)直接從它里面去取类腮。preorderedList怎么來(lái)?
通過(guò)調(diào)用buildOrderedChildList方法獲取的蛉加。buildOrderedChildList方法是怎么樣的蚜枢?
它里面是通過(guò)一個(gè)getAndVerifyPreorderedIndex方法來(lái)獲取對(duì)應(yīng)的子View索引缸逃,這個(gè)方法要傳進(jìn)去一個(gè)叫customOrder的boolean值。這個(gè)customOrder厂抽,看名字可以知道需频,是自定義順序的意思,如果它為true的話筷凤,接著會(huì)通過(guò)getChildDrawingOrder(int childCount, int i)方法來(lái)獲取對(duì)應(yīng)的索引昭殉,而且,這個(gè)方法是protected的藐守,所以我們可以通過(guò)重寫這個(gè)方法并根據(jù)參數(shù)"i"來(lái)決定返回哪一個(gè)View所對(duì)應(yīng)的索引挪丢,從而改變分發(fā)的順序。那這個(gè)customOrder卢厂,什么時(shí)候?yàn)閠rue呢乾蓬?
在buildOrderedChildList方法里可以看到這么一句:final boolean customOrder = isChildrenDrawingOrderEnabled();
emmmm,也就是說(shuō)慎恒,如果要自定義這個(gè)順序的話任内,還需要調(diào)用setChildrenDrawingOrderEnabled(true)來(lái)開(kāi)啟。
重新捋一捋流程:
- setChildrenDrawingOrderEnabled(true)來(lái)開(kāi)啟自定義順序
- 重寫getChildDrawingOrder方法來(lái)決定什么時(shí)候要返回哪個(gè)子View
使用場(chǎng)景
常用的SwipeRefreshLayout融柬、ViewPager死嗦、RecyclerView都實(shí)現(xiàn)getChildDrawingOrder方法。其中RecyclerView還可以通過(guò)一個(gè)setChildDrawingOrderCallback方法來(lái)動(dòng)態(tài)指定順序粒氧,而不用重寫RecyclerView越除。