Android App开发进阶与项目实战
上QQ阅读APP看书,第一时间看更新

2.4.1 上下滚动与左右滑动的冲突处理

Android控件繁多,允许滚动或滑动操作的视图也不少,例如滚动视图、翻页视图等,如果开发者要自己接管手势处理,比如通过手势控制横幅(Banner)轮播,那么这个页面的滑动就存在冲突的情况,如果系统响应了A视图的滑动事件,就顾不上B视图的滑动事件。

举个例子,某电商App的首页很长,内部采用滚动视图,允许上下滚动。该页面中央有一个手势控制的横幅轮播,如图2-25所示。用户在横幅上左右滑动,试图查看横幅的前后广告,结果如图2-26所示,原来翻页不成功,整个页面反而往上滚动了。

图2-25 滚动视图中的横幅轮播

图2-26 翻页滑动导致上下滚动

即使多次重复试验,仍然会发现横幅很少跟着翻页,而是继续上下滚动。因为横幅外层被滚动视图包着,系统检测到用户手势的一撇,父视图—滚动视图自作主张地认为用户要把页面往上拉,于是页面往上滚动,完全没有考虑这一撇其实是用户想翻动横幅。但是滚动视图不会考虑这些,因为没有人告诉它超过多大斜率才可以上下滚动;既然没有通知,那么滚动视图只要发现手势事件前后的纵坐标发生变化就一律进行上下滚动处理。

要解决这个滑动冲突,关键在于提供某种方式通知滚动视图,告诉它什么时候可以上下滚动、什么时候不能上下滚动。这个通知方式主要有两种:一种是父视图主动向下“查询”,即由滚动视图判断滚动规则并决定是否拦截手势;另一种是子视图向上“反映”,即由子视图告诉滚动视图是否拦截手势。下面分别介绍这两种处理方式。

(1)由滚动视图判断滚动规则

前两节提到,容器类视图可以重写onInterceptTouchEvent方法,根据条件判断结果决定是否拦截发给子视图的手势。那么可以自定义一个滚动视图,在onInterceptTouchEvent方法中判断本次手势的横坐标与纵坐标,如果纵坐标的偏移大于横坐标的偏移,此时就是垂直滚动,应拦截手势并交给自身进行上下滚动;否则表示此时为水平滚动,不应拦截手势,而是让子视图处理左右滑动事件。

下面的代码演示了自定义滚动视图拦截垂直滚动并放过水平滚动的功能。

(完整代码见event\src\main\java\com\example\event\widget\CustomScrollView.java)

接着在布局文件中把ScrollView节点改为自定义滚动视图的完整路径名称(如com.example.event.widget.CustomScrollView),重新运行App后查看横幅轮播,手势滑动效果如图2-27所示。此时翻页成功,并且整个页面固定不动,未发生上下滚动的情况。

图2-27 翻页滑动未造成上下滚动

(2)子视图告诉滚动视图能否拦截手势

在目前的案例中,滚动视图下面只有横幅一个淘气鬼,所以允许单独给它“开小灶”。在实际应用场合中,往往有多个“淘气鬼”,一个要吃苹果,另一个要吃香蕉,倘若都要滚动视图帮忙,那可真是忙都忙不过来了。不如弄个水果篮,想吃苹果的就拿苹果,想吃香蕉的就拿香蕉,如此皆大欢喜。

具体到代码的实现,需要调用requestDisallowInterceptTouchEvent方法(输入参数为true时表示禁止上级拦截触摸事件)。至于何时调用该方法,当然是在检测到滑动前后的横坐标偏移大于纵坐标偏移时。对于横幅采用手势监听器的情况,可重写onTouchEvent方法(在该方法中加入坐标偏移的判断),示例代码如下:

(完整代码见event\src\main\java\com\example\event\widget\BannerPager.java)

修改后的手势滑动效果参见图2-27。左右滑动能够正常翻页,整个页面也不容易上下滚动。