首先来看一个场景,一个LinearLayout里面放了一个Button,手势从Button上按下,然后移动到Button外面。
从Button滑出来这个过程,涉及了一些事件的传递。
首先从事件传递的U型图可以知道,如果我们不对ViewGroup与View的onInterceptTouchEvent、dispatchTouchEvent方法做自定义修改的话,那么事件传递的逻辑是分发到最下层的View,又最下层的View消费掉的。
查看我们常用的RelativeLayout与LinearLayout的源代码可以知道,这两个布局并没有去重写父类的dispatchTouchEvent与onInterceptTouchEvent方法。
所以从Button滑动到Button外的过程中,Button首先消费了Down的事件,以至于后续的事件序列Move、Up事件都是由Button处理,即使手指已经划出Button外面了。
但是某些容器类,如ScrollView与ListView类是不一样的,它们重写过onInterceptTouchEvent的方法。会拦截Move事件,并且将一个Move事件转化为Cancel事件,并且传递给Button(子View),之后的Move事件将会由他自己处理。
当然有关于特殊的滚动容器类(ScrollView与ListView)虽然继承于ViewGroup,但是相关的事件分发方法都会去重写。
在遇到的滑动冲突,往往都是因为重写的事件分发机制引起的,而我们要做的就是,明确指定在该嵌套场景下的事件处理对象是谁,就可以解决这些冲突问题。
在实际的开发场景里,最常见到两类的滑动冲突场景。
Android的事件机制告诉我们,事件传递是一个U型模型,一般来说,事件不被特意拦截的情况下,会下发到最底下的View,而子View如果处理不了,再向上传递交由上层处理。
不同方向出现的滑动冲突是什么原因造成的?
1、外层水平方向可以滑动ViewGroup将事件下发给了ListView。
2、ListView的onInterceptTouchEvent返回了true给上层,ListView接受了下发的DOWN事件,整个事件序列都将由ListView处理。
所以即使我们在水平滑动产生的事件都将由ListView处理了,导致无法水平滑动的冲突场景出现。
解决:
1、比较水平与竖直方向的位移确定是否拦截事件。
2、在上层ViewGroup的onInterceptTouchEvent方法中拦截或者在当前的view中的dispatchTouchEvent拦截。
同一方向可以用ScrollView嵌套ListView演示
ScrollView的onInterceptTouchEvent拦截了事件,导致事件不再分发下去,自身就消费掉了。所以ListView无法获得事件。
解决:
1、同向滑动冲突需要根据具体的业务来编写条件算法。
2、和不同方向一样,也可以通过内部和外部拦截去实现。
具体实例,可参考Android事件分发机制及滑动冲突解决方案