处理View滑动冲突的一种方法

  当界面中内外两层View都可以滑动时,就会产生滑动冲突。比如一个列表型的ViewGroup里面盛放了若干个View,ViewGroup可以垂直滚动,而里面的View又支持水平滑动的手势,当用户在ViewGroup和View的重叠区域滑动手指时,就会产生滑动冲突——无法判断当前滑动操作应当由ViewGroup还是View来进行处理。本文通过一个简单的例子,说明滑动冲突的一种处理方法。

1. 构造滑动冲突场景

  首先来构造滑动冲突的场景。这里使用的场景如前所述,在VerticalScrollView中放置BlockView,VerticalScrollView要支持垂直滚动,BlockView要支持水平滑动,由此产生滑动冲突。布局文件包含一个VerticalScrollView:

【完整文件】

  VerticalScrollView中放置的item布局如下:

【完整文件】

  然后填充VerticalScrollView:

2. 通过自定义ViewGroup对触摸事件的拦截解决滑动冲突

  对触摸事件的拦截处理是解决滑动冲突的关键。简单来说,被ViewGroup拦截的触摸事件不会被传递到其子元素中。在当前场景,如果VerticalScrollView不加区分地拦截了所有滑动事件,那么VerticalScrollView的垂直滚动是可以正常进行的,但在其中的BlockView的水平滑动的手势就会失效。

  由于VerticalScrollView和BlockView所支持的滑动方向不同,那么就可以根据滑动方向,来判断应当把触摸事件交给谁处理。在VerticalScrollView的 onInterceptTouchEvent() 中:

  1. 当发生 MotionEvent.ACTION_DOWN ,用户手指按下屏幕,此时不进行拦截,因为一旦拦截 MotionEvent.ACTION_DOWN ,当次事件序列之后的所有触摸事件都会被拦截,相当于VerticalScrollView拦截了所有触摸事件;
  2. 当发生 MotionEvent.ACTION_MOVE ,用户手指在屏幕上移动,获取当前移动的垂直距离和水平距离,只有当垂直滑动距离大于水平滑动距离时,认为当前用户在进行垂直滑动,才对触摸事件进行拦截;
  3. 当发生 MotionEvent.ACTION_UP ,用户手指离开屏幕,取消拦截。

  具体代码如下:

【完整文件】

  完整代码可以在这里找到。

3. 解决滑动冲突的其他方法

  在《Android开发艺术探索》一书中,给出了两种解决滑动冲突的方法:一种是外部拦截法,在父容器中有选择地拦截事件,也就是上面使用的方法;还有一种内部拦截法,父容器不拦截任何事件,全部事件都交给子元素,如果子元素需要处理当前的触摸事件,就直接消耗掉,否则就交给父容器处理,需配合 requestDisallowInterceptTouchEvent() ,略显复杂。