ViewPager, CoordinatorLayout与RecyclerView一起使用时滑动的Bug

现象

假设一个页面而已是这样的

          ViewPager
              |
Fragment  Fragment    Fragment
              |
       CoordinatorLayout
              |
         RecyclerView

向上滑动一点RecyclerView,然后再快速下拉,让RecyclerView快速fling到顶部,到顶部后RecyclerView会停止滚动,这时马上左右滑,会发现ViewPager无法左右滑动,要等2-3秒,或手指触摸一下屏幕后,ViewPager才可以响应左右滑动。

如果RecyclerView外部没有NestedScrollingParent或RecyclerView禁用了NestedScroll就没问题。

原因

RecyclerView滑动到顶部后,虽然滑动已经停止了,但其实内部的状态还是STATE_SETTING,而不是STATE_IDLE,所以下次的TouchEvent会直接给RecyclerView。

因为fling时RecyclerView并不知道最终能fling多久,所以是尝试性质的,没有NestedScroll时,RecyclerView判断到某次没滑动了就直接自己终止了滑动事件,回到STATE_IDLE状态。而外部有嵌套NestedScrollingParent时,RecyclerView并没有判断外部的滑动有没有结束,所以滑到顶部时没有自己终止滑动。

具体代码就不帖了,看的时间比较久了,懒得再翻一遍了。

解决方法

告诉RecyclerView嵌套滑动的状态,继承RecyclerView,重写dispatchNestedScroll方法,外部没滑动时返回fales

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed,
int dyUnconsumed, int[] offsetInWindow, int type) {
if (offsetInWindow == null) {
offsetInWindow = mOffsetInWindow;
offsetInWindow[0] = offsetInWindow[1] = 0;
}
boolean result = super.dispatchNestedScroll(dxConsumed, dyConsumed,
dxUnconsumed, dyUnconsumed, offsetInWindow, type);
//noinspection SimplifiableIfStatement
if (result && (dxConsumed == 0 && offsetInWindow[0] == 0) && (dyConsumed == 0 && offsetInWindow[1] == 0)) {
// consumed == 0表示RecyclerView自己没滚动,offsetInWindow == 0表示要Parent滚动但Parent没滚动
// 这种情况下就认为滚动结束了
return false;
}
return result;
}