前言
和同事一起开发了一个自己的树洞App,由于功能需要,需要实现类似于知乎的上一条、下一条滑动的效果,首先想到的就是ViewPager,来实现垂直滑动,但是ViewPager默认是左右滑动的,如果用的话,需要对ViewPager进行重写(网上有很多方案,这里就不做说明了),来进行上下滑动,所以就想到了其他方法。比如RecyclerView也能够来实现,不过也需要对PagerSnapHelper进行操作。因为item里面需要用到刷新控件来实现上一条、下一条的效果,所以也就放弃了RecyclerView。最终,选中了ViewPager2来实现这个功能
ViewPager2说明
viewPager2是谷歌新出的控件,相当于ViewPager+RecyclerVIew的结合,内部是对RecyclerVIew的封装,也可以直接使用RecyclerView.Adapter来进行数据适配,帮我们实现了一些ViewPager的效果,所以很符合我们的需求,方便快捷。
问题
由于Item中有ScrollerView控件,需要实现上下滑动超过一屏的情况,所以从第一条Item滑动到第二条Item之后,再滑回来,由于复用的问题,导致第一条Item的数据显示在最低端,影响用户的体验,希望能够在每次切换的时候,用户看到的UI永远都是当前Item的最顶端。这样的话,直接在Adapter中对ScrollView进行操作肯定是没有效果的,只有被销毁的Item再次创建才会符合我们的要求。
解决思路
根据上面的问题,想到了解决的思路:那就是获取当前选中的Item,然后拿到item中的View,比如ScrollerView,对其进行scrollTo(0,0)就能够满足我们的需求了嘛。按照这个思路,进行了调试,发现由于RecyclerView的Adapter复用的机制,导致没办法直接拿到Item中的View,在网上找了多种解决方案都不行,没办法实时拿到当前选中的Item。最后,想到了曲线救国,既然ViewPager2是对RecyclerVIew进行的封装,那么能不能通过RecyclerVIew的一些方法进行解决了,就去查了一下关于RecyclerView的获取指定item的View的方案,发现还是有方法的。
解决方案
一开始用的是
(viewPager2.adapter as SquareAp).getViewByPosition(viewPager2.getChildAt(0),position,R.id.scrollerView)
因为getViewByPosition需要传RecyclerView的对象,所以才会有viewPager2.getChildAt(0)这个方法。
通过验证,发现不行,获取到的View控件有时会为null,应该还是和复用有关系。
没办法,只能修改方案了。
通过查找发现,RecyclerVIew可以通过layoutManager的findViewByPosition方法来获取到指定Item的View。大喜!这不就是我们想要的嘛?既然找到的方法,那就上手干吧!
先通过ViewPager2获取到内部的RecyclerVIew的对象
val recyclerView= viewPager2.getChildAt(0) as RecyclerView
在通过recyclerVIew调用layoutManager的findViewByPosition方法获取到指定position对象的View
val view = recyclerView.layoutManager?.findViewByPosition(position)
到了这一步,基本就解决了我们的问题。
最后,通过View找到scrollerView
val scrollView = view?.findViewById<NestedScrollView>(R.id.scroll_view)
这样,我们就可以对scrollerVIew进行操作了
scrollView.scrollTo(0, 0)
到此,我们的问题就完美结局了。
还有一点要说明一下,ViewPager2的滑动监听方法和VIewPager有很大区别。
最后
奉上关于ViewPager2中获取指定Item的View控件的完整代码
viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
if (position < selectPosition) {
//往上滑
GlobalScope.launch(Dispatchers.IO) {
delay(50)
val view = recyclerView.layoutManager?.findViewByPosition(position)
val scrollView = view?.findViewById<NestedScrollView>(R.id.scroll_view)
if (scrollView != null) {
withContext(Dispatchers.Main) {
scrollView.scrollTo(0, 0)
}
}
}
}
selectPosition = position
}
})
如果你有其他好的解决方案,欢迎留言或+好友讨论…