项目中有动态改变fragment顺序和配置fragment数量的需求,以前用的viewPager,也不是不能用,但是了解到了viewpager2,我就手贱升级了。
谷歌官方文档地址:谷歌发布viewPager2发布1.0.0稳定版本是2019年11月20日,我写博客的时间是2021年4月。已经过去了这么久了,晚上能查到的文献还是非常的少,而且遇到了一些坑,在网上并不能找到解决办法。
下面附上谷歌文档
https://developer.android.google.cn/jetpack/androidx/releases/viewpager2
关于ViewPager2到底有哪些改进,这里就不赘述了,上面链接里都有,网上文章也很多。
目录
一、ViewPager2真香的地方
1,垂直滚动
以前找各种轮子,现在一个属性, android:orientation="vertical"
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp_video"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
2,notifyDatasetChanged()
动态配置fragment数量,或者更改fragment顺序
参考的这个大哥的文章,https://zhuanlan.zhihu.com/p/105700960
注意啊,如果存放ID的hashset没有声明类型,里面的object一定要是统一类型(int或者long之类的)。我就是写错了查了很久的BUG。
二、ViewPager2踩坑
1,滑动嵌套冲突
ViewPager2是基于recyclerView实现的,ViewPager2中嵌套ViewPager2,或者ViewPager2中嵌套recyclerView时,出现滑动卡顿,解决办法是在内层ViewPager2或recyclerView外套一层NestedScrollableHost,NestedScrollableHost的代码是我从kotlin自动转成java的,所以看着很傻缺,但是直接复制可以用就是了。
<com.test.demo.NestedScrollableHost
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rcl_video"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.test.demo.NestedScrollableHost>
public class NestedScrollableHost extends FrameLayout {
private int touchSlop;
private float initialX;
private float initialY;
private HashMap _$_findViewCache;
public NestedScrollableHost(Context context) {
super(context);
ViewConfiguration var10001 = ViewConfiguration.get(this.getContext());
this.touchSlop = var10001.getScaledTouchSlop();
}
public NestedScrollableHost(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
ViewConfiguration var10001 = ViewConfiguration.get(this.getContext());
this.touchSlop = var10001.getScaledTouchSlop();
}
private final ViewPager2 getParentViewPager() {
ViewParent var10000 = this.getParent();
if (!(var10000 instanceof View)) {
var10000 = null;
}
View v;
for (v = (View) var10000; v != null && !(v instanceof ViewPager2); v = (View) var10000) {
var10000 = v.getParent();
if (!(var10000 instanceof View)) {
var10000 = null;
}
}
View var2 = v;
if (!(v instanceof ViewPager2)) {
var2 = null;
}
return (ViewPager2) var2;
}
private final View getChild() {
return this.getChildCount() > 0 ? this.getChildAt(0) : null;
}
private final boolean canChildScroll(int orientation, float delta) {
boolean var5 = false;
int direction = -((int) Math.signum(delta));
View var10000;
boolean var6 = false;
switch (orientation) {
case 0:
var10000 = this.getChild();
var6 = var10000 != null ? var10000.canScrollHorizontally(direction) : false;
break;
case 1:
var10000 = this.getChild();
var6 = var10000 != null ? var10000.canScrollVertically(direction) : false;
break;
default:
// throw (Throwable)(new IllegalArgumentException());
}
return var6;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent e) {
this.handleInterceptTouchEvent(e);
return super.onInterceptTouchEvent(e);
}
private final void handleInterceptTouchEvent(MotionEvent e) {
ViewPager2 var10000 = this.getParentViewPager();
if (var10000 != null) {
int orientation = var10000.getOrientation();
if (this.canChildScroll(orientation, -1.0F) || this.canChildScroll(orientation, 1.0F)) {
if (e.getAction() == 0) {
this.initialX = e.getX();
this.initialY = e.getY();
this.getParent().requestDisallowInterceptTouchEvent(true);
} else if (e.getAction() == 2) {
float dx = e.getX() - this.initialX;
float dy = e.getY() - this.initialY;
boolean isVpHorizontal = orientation == 0;
boolean var8 = false;
float scaledDx = Math.abs(dx) * (isVpHorizontal ? 0.5F : 1.0F);
boolean var9 = false;
float scaledDy = Math.abs(dy) * (isVpHorizontal ? 1.0F : 0.5F);
if (scaledDx > (float) this.touchSlop || scaledDy > (float) this.touchSlop) {
if (isVpHorizontal == scaledDy > scaledDx) {
this.getParent().requestDisallowInterceptTouchEvent(false);
} else if (this.canChildScroll(orientation, isVpHorizontal ? dx : dy)) {
this.getParent().requestDisallowInterceptTouchEvent(true);
} else {
this.getParent().requestDisallowInterceptTouchEvent(false);
}
}
}
}
}
}
public View _$_findCachedViewById(int var1) {
if (this._$_findViewCache == null) {
this._$_findViewCache = new HashMap();
}
View var2 = (View) this._$_findViewCache.get(var1);
if (var2 == null) {
var2 = this.findViewById(var1);
this._$_findViewCache.put(var1, var2);
}
return var2;
}
public void _$_clearFindViewByIdCache() {
if (this._$_findViewCache != null) {
this._$_findViewCache.clear();
}
}
}
2,crash
vp2嵌套了vp2出现的BUG,我本地无法复现,却在BUGLY统计到了数十个crash。
为解决此BUG,我把外层不需要动态配置fragment的viewPager2改回了viewPager。
知道怎么解决的大佬求告知
3,预加载问题
我们知道setOffscreenPageLimit(N)方法可以设置预加载的数量,且在N个之内的fragment不会被回收,那么我就是不想预加载怎么办呢。
现在有两个fragment A和B,不想在A显示的时候,初始化Bfragment。
viewPager2是可以滑动翻页的,在这种情况下,不预加载相邻fragment是不可能的,只能在fragment OnResume的时候去调用接口之类的,避免不必要的开销。
取消VP2通过手势滑动页面,VP2支持取消滑动,setUserInputEnabled()方法就搞定
4,去掉滑动阴影
我们知道recyclerView支持在xml中配置一个android:overScrollMode="never"属性就搞定了,VP2不是基于recyclerView实现的吗?!但是却并没有这个属性,只能在代码中写了。
View child = viewPager2.getChildAt(0);
if (child instanceof RecyclerView) {
child.setOverScrollMode(View.OVER_SCROLL_NEVER);
}
三、升级到VP2
目前涉及viewPager嵌套ViewPager的UI,个人不太建议将库升级到ViewPager2。
如果非要换,只建议把有动态配置Fragment需求的ViewPager升级到2。
最后附上官方文档,将应用中的 ViewPager 对象更新为 ViewPager2
https://developer.android.google.cn/training/animation/vp2-migration?hl=zh_cn