登录

去注册

登录

注册

去登录

注册

每日一问 事件分发机制大家应该都熟记于心,默认事件分发是逆序的,有哪些方法可以修改分发顺序?

xiaoyang   2019-08-11   收藏

记得曾经有位同学做贴纸应用时,有RT 的需求。

默认事件分发为逆序,遍历子 View 为 (childCount ~ 0 ],有哪些方式可以修改这一策略,比如修改遍历方式为[0,childCount)?

3/3

12

修改事件分发顺序的话,在日常开发中基本遇不到,因为现在的逆序遍历,是跟View的层级显示相匹配的,随便更改反而不太合理。

如果非要修改这个顺序,很多同学首先会想到:重写dispatchTouchEvent方法,然后在里面一个for循环,从0开始一个个调用子View的dispatchTouchEvent。

这个方法,不是说绝对不行,只是你要做的事情很多,就比如触摸坐标的转换:
我们都知道,ViewGroup在分派事件的时候,会检查子View是否应用过属性动画的(位移、缩放、旋转等),如果有的话还要把坐标给映射回去。
接着,还会把相对于这个ViewGroup本身的触摸坐标 转换成 相对于对应子View的触摸坐标。这样说可能有点绕,
举个例子,比如:当手指在屏幕中按下,ViewGroup中收到的event坐标(getX,getY)假设是【500,500】,刚好在这个位置上有个子View,那接下来肯定会把事件传给这个子View的dispatchTouchEvent,这时候如果坐标不转换直接传的话,那子View收到的event坐标(getX,getY)也是【500,500】,这明显是不对的,正确的坐标应该要分别减去它的left和top。
这看起来好像没什么大的影响,但如果你的子View没有重写onTouchEvent方法的话(比如子View是常用的ImageView,TextView之类的),你的OnClickListener就会无效了,因为默认的onTouchEvent在处理ACTION_MOVE的时候,会检查event的坐标是否已经脱离了View的边界范围,如果在边界范围之外的话,pressed将会失效(认为没有被按下),当ACTION_UP时,如果pressed为false,就不会执行PerformClick。

那难道没有方法可以完美地做到了吗?
在ViewGroup的dispatchTouchEvent方法中,虽然它是逆序的for,但是呢,它把子View拿出来的时候,却不是直接操作的mChildren数组,
而是通过一个getAndVerifyPreorderedView方法来获得,这个方法会把当前索引传进去,还有一个preorderedList。
如果传进去的preorderedList不为空,那么就会直接从它里面去取。
preorderedList怎么来?
通过调用buildOrderedChildList方法获取的。
buildOrderedChildList方法是怎么样的?
它里面是通过一个getAndVerifyPreorderedIndex方法来获取对应的子VIew索引,这个方法要传进去一个叫customOrder的boolean。
这个customOrder,看名字可以知道,是自定义顺序的意思,如果它为true的话,接着会通过getChildDrawingOrder(int childCount, int i)方法来获取对应的索引,
而且,这个方法是protected的,所以我们可以通过重写这个方法并根据参数"i"来决定返回哪一个View所对应的索引,从而改变分发的顺序。
那这个customOrder,什么时候为true呢?
在buildOrderedChildList方法里可以看到这么一句:

final boolean customOrder = isChildrenDrawingOrderEnabled();

emmmm,也就是说,如果要自定义这个顺序的话,还需要调用setChildrenDrawingOrderEnabled(true)来开启。
重新捋一捋流程:

  1. setChildrenDrawingOrderEnabled(true)来开启自定义顺序;
  2. 重写getChildDrawingOrder方法来决定什么时候要返回哪个子View;
回复
杨正友 : @陈小缘 

陈小缘,也太牛逼啦。。。。

2019-08-31 回复
tustlitao : @陈小缘 

好秀啊

2019-08-09 回复
陈小缘 : @鸿洋 

哈哈哈哈哈哈

2019-08-09 回复
鸿洋 : @陈小缘 

哈哈 小缘是真牛逼,我预期就是不希望大家重写dispatchTouchEvent来实现,毕竟分发逻辑最好不要轻易修改,就希望通过修改order实现,完全命中我的预期。  ...查看更多

2019-08-09 回复
陈小缘 : @陈小缘 

常用的SwipeRefreshLayout、ViewPager、RecyclerView都实现getChildDrawingOrder方法。其中RecyclerView还可以通过一个setChildD  ...查看更多

2019-08-09 回复

删除留言

确认删除留言,会导致相关评论丢失?

取消 确定