萌萌の初音
萌萌の初音
发布于 2023-02-25 / 996 阅读
0

ViewPager2+短视频引发的悲伤故事

公司短视频项目新加手势左右滑动切换短视频分类的需求,当时我毫不犹豫的选择了ViewPager2进行实现,但这个选择给我买下了许多的坑。

1.手势冲突

因为有几个页面带有横向滑动的功能,加入了ViewPager2后,滑动功能区就直接把页面切换了,我直接喵喵喵???想想和RecyclerView冲突了而已,重写OnTouchListener不就完了,当我心满意足的写完bug后开始测试,啪的一下很快啊,页面直接就切了,要放在功能区停留一下再滑动才行,我心想这玩意体验不行啊,得想办法优化才行啊,既然从OnTouchListener不行,就从ViewPager2的滑动灵敏度下手(为啥不在父View或子View实现?当时项目页面类型太多,动的话每个都要动就没考虑),但ViewPager2是final类,只有从反射下手了。

inline fun ViewPager2.desensitization(sensitivity: Int){
	//动态设置ViewPager2 灵敏度
	runCatching {
		val recyclerViewField = ViewPager2::class.java.getDeclaredField("mRecyclerView")
		recyclerViewField.isAccessible = true
		val recyclerView = recyclerViewField.get(this) as RecyclerView
		val touchSlopField = RecyclerView::class.java.getDeclaredField("mTouchSlop")
		touchSlopField.isAccessible = true
		val touchSlop = touchSlopField.get(recyclerView) as Int
		touchSlopField.set(recyclerView, touchSlop * sensitivity) //6 is empirical value
	}
}

通过此扩展方法,降低了ViewPager2的滑动灵敏度解决了横向滑动冲突的问题。(还是要重写手势事件哦,配合一起工作)

2.多重音频大轰炸

为了优化滑动体验offscreenPageLimit设置到3(兼顾左右各一页缓存),但离谱的是慢慢滑动,下一页的fragment就开始播放短视频了,然后两种声音开始在手机扬声器里发出(绝望)。
随即手动加入初始化调用方法,在滑动完成后通过OnPageChangeCallback回调positon对指定的fragment调用初始化方法,随即解决问题。但有人看到这就会说,你fragment咋拿啊,难道你丫的还把fragment存下来了?有你内存泄漏吃!那肯定不会啊,ViewPager2的adapter内部实现通过fragmentManager添加fragment,并给fragment设置了tag,格式为"f${fragmentAdapter.getItemId(viewPager.currentItem)}",通过findFragmentByTag即可找到自己需要的fragment。

3.跨页面重复构建两次fragment问题

当你的ViewPager2的fragment足够多,从第一页切换到最后一页,再切回第一页,就会发现fragment重建了两次,且hashCode与最开始的都不一样,通过fragmentAdapter.getItemId(viewPager.currentItem)只能找到最后被赋予此tag的fragment,另一个就在同一个页面开始疯狂播放视频了无法找到控制……
如何解决呢?通过切换页面的position将左右相邻超过2个的fragment用fm.remove掉,这样就不会出现重建两个相同fragment的情况了,为什么会出现呢?主要是FragmentStateAdapter通过saveState恢复了fragment,但又再次新建了一个无状态的fragment导致,从api文档看ViewPager2一直万能不变在1.0.0,最新的1.1.0还处于beta版,新修复了与新版RecyclerView的bug和支持在adapter里进行状态管理,但毕竟是beta版不敢用在线上项目,只能硬着头皮炫了remove方法(绝望)。

以上就是ViewPager2+短视频开发印象最深的坑了,记录一下