I'm using a ViewPager2
and a ListAdapter
to scroll horizontally through Recycler.ViewHolder
items, showing one item at a time. Each can be scrolled vertically when the content doesn't fit on the screen.
I would like to reset the scroll state of the items once they are not visible anymore, because the view holder is likely to be reused for another item, and because it's tidier.
I know how to reset the scroll state in the ViewHolder
, but where can I put this code?
I first thought it could be in the fragment hosting the ViewPager2
, where I could register a callback ViewPager2.OnPageChangeCallback
with the viewpager:
private var lastPosition = -1
private var callback = object: ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
if (lastPosition >= 0) {
// reset scroll here
val v: MyViewHolder = ???(position)
v.resetScrollState()
}
lastPosition = position
}
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewpager = binding.itemPager
viewpager.adapter = [...]
viewpager.registerOnPageChangeCallback(callback)
}
(where binding.itemPager
points to the ViewPager2
)
But I don't know how to easily get to the view holder, except by storing it (in an collection for ex.) so I can retrieve it from position
. Then I would still have to correctly set lastPosition
for the first time but it's secondary.
My question: Is there any way to
position
?Or is there a better way to handle this?
I found it eventually.
For a given lastPosition
, the view holder can be obtained with:
(viewpager.get(0) as RecyclerView).findViewHolderForAdapterPosition(lastPosition)
as? MyViewHolder
So the callback becomes:
private var callback = object: ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
if (lastPosition >= 0) {
// reset scroll here
val holder = (viewpager.get(0) as RecyclerView).findViewHolderForAdapterPosition(lastPosition) as? TodoEventAdapter.MyViewHolder
if (holder != null)
holder.resetScroll()
}
lastPosition = position
}
}
I can set the initial value of lastPosition
and register the callback, for example in the fragment code with ViewPager2.currentItem
:
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewpager = binding.itemPager
[...]
lastPosition = viewpager.currentItem
viewpager.registerOnPageChangeCallback(callback)
}
override fun onDestroyView() {
[...]
viewpager.unregisterOnPageChangeCallback(callback)
super.onDestroyView()
}
Where binding.itemPager
uses viewBinding, and corresponds to the ViewPager2 instance of the layout.
Note that the first position to show is likely to come from a parameter, for example an activity that needs to display a specific item at a starting position. The initial value of lastPosition
may be obtained from this parameter, or from the ViewPager2.currentItem
value once/if it has been set, as shown above.