ViewPager 滑动冲突分析与解决

前言

ViewPager 的滑动的效果比较不错,常与 Fragment 搭配实现选项卡页面,然而有时候
会出现滑动冲突的情况,具体表现为有时候滑不动,下面就最近遇到的情况分析下其中的原因。

问题描述

在一个 ViewPager 里面有4个 Fragment,第二个 Fragment 里面有一些 TextView 组件,如下图:

结果发现在这些 TextView 之间空白的地方可以水平滑动页面,但是在 TextView 上面就无法滑动了。

原因分析

发现这个问题的时候一脸懵逼,死活想不通是哪里的原因。于是网上搜索一番,有个说法是去掉 TextViewandroid:singleLine="true" 属性即可,一开始我是不信的,这个属性跟滑动无效似乎没有半毛钱的关系,持着怀疑的态度试了下,问题居然解决了!

现在更是想不通了,顺着搜索结果来到了 Stackoverflow 上找原因,有如下这段话:

if the view can scroll horizontally, it intercepts the horizontal motion event and ViewPager is not able to process it anymore.

Since API Level 14 TextViews have android:scrollHorizontally property (and setHorizontallyScrolling(boolean) method), which, if set to true, causes the TextView to intercept horizontal scroll motion events.

You may set it to false either in XML or right in the code, but watch out: android:singleLine property forces android:scrollHorizontally to be set to true! Very tricky point! But fortunately, you usually able to safely replace single line property with android:maxLines=”1” and necessary ellipsize value.

看到这里终于知道了为什么。上面这段话的大概意思是如果 ViewPager 里面的组件可以水平滚动,它们将拦截水平滚动的事件,致使 ViewPager 无法接收到水平滚动事件,出现滑动无效的情况。从 API 14 开始,TextView 增加了水平滚动的属性 android:scrollHorizontallysetHorizontallyScrolling(boolean) 方法,当该属性为 true 的情况下,TextView 就会拦截水平滚动事件。但是有个恶心的地方,当设置了 android:singleLine="true" 的时候,android:scrollHorizontally 也将自动变成 true!万幸的是,可以使用 android:maxLines="1" 等效替代 android:singleLine="true"

根据上面的结论,总结下 ViewPager 滑动冲突的解决思路。Android 系统触摸事件的处理流程大概是这样的,系统收到触摸事件之后,从点击的组件开始,一层层往最外层的组件进行分发处理,一旦有组件处理消耗了该事件,则结束。如果出现了触摸事件没有预期出现响应的时候,就该考虑下是否是里面的组件拦截了事件。

好记性不如烂笔头,随手记录下,以防忘记。