背景
自去年12月份ViewPager2正式版发布以后,ViewPager2已经逐渐开始替代旧版本的ViewPager。许多开发者也已经在项目中使用了ViewPager2。相比ViewPager,ViewPager2的功能不可谓不强大。
我在之前写过的一篇文章
学不动也要学!深入了解ViewPager2:
https://zhpanvip.gitee.io/2019/12/14/24.Know%20about%20ViewPager2/
对ViewPager2的使用做过详细的讲解。但是,由于当时没有太多实战,所以并没有发现ViewPager2的嵌套使用存在严重的滑动冲突。
直到今年三月份用ViewPager2重构BannerViewPager的时候才发现这个问题。因此,在BVP 3.0版本中额外对ViewPager2做了滑动冲突处理,效果还算差强人意。另外,曾在论坛上看到过不少ViewPager2滑动冲突的求助帖子,甚至还有同学因为搜索ViewPager2滑动冲突而找到了BannerViewPager的Github主页。既然如此,不如写篇文章将BVP处理滑动冲突的经验分享给大家,没准还能涨知 (fěn) 识 (sī),嘿嘿嘿。
为什么ViewPager没有冲突
不知道你是否有这个疑问,在ViewPager时代,ViewPager嵌套ViewPager并没有出现过滑动冲突。可是为什么在ViewPager的升级版ViewPager2中却出现了滑动冲突呢?想要搞清楚这个问题就需要我们深入到ViewPager和ViewPager2的内部分析一下它们的源码了。
我们知道,滑动冲突是需要在onInterceptTouchEvent方法中进行处理的,根据自身条件来决定是否要拦截事件。在ViewPager的源码中看到以下代码(方便阅读,代码做了删减):
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction() & MotionEvent.ACTION_MASK;
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// 在事件取消或者抬起手指后重置状态
resetTouch();
return false;
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
// 这里判断在水平方向上的滑动距离大于竖直方向的2倍,则认为是有效的切换页面的滑动
if (xDiff > mTouchSlop && xDiff * 0.5f > yDiff) {
mIsBeingDragged = true;
// 禁止Parent View拦截事件,即事件要能够传递到ViewPager
requestParentDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
} else if (yDiff > mTouchSlop) {
mIsUnableToDrag = true;
}
break;
}
case MotionEvent.ACTION_DOWN: {
if (mScrollState == SCROLL_STATE_SETTLING
&& Math.abs(mScroller.getFinalX() - mScroller.getCurrX()) > mCloseEnough) {
// 在Down事件中禁止Parent View拦截事件,是为了事件序列能够传递到ViewPager
requestParentDisallowInterceptTouchEvent(true);
setScrollState(SCROLL_STATE_DRAGGING);
} else {
completeScroll(false);
mIsBeingDragged = false;
}
break;
}
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
return mIsBeingDragged;
}