I am working with a RecyclerView and a corresponding LinearLayoutManager. I was adding some custom scrolling features to part of my app where I would translate a custom header object along with the scroll of the RecyclerView modeled after this project: https://github.com/boxme/ParallaxHeaderViewPager (which uses ListView instead of RecyclerView).
I ran into a weird issue though. It would scroll just fine for a while but then it would jump a few hundred pixels. I added log statements to see the offset computed by LinearLayoutManager.computeVerticalScrollOffset() as I scrolled as sure enough, the offset was randomly jumping from like 320 to 1200 then it would continue computing the offset appropriately from that point.
See my answer below on how I solved this!
Thanks to this thread finally found a way to resolve this accurately, posting below the code for the overriden LinearLayoutManager, sorry its kotlin though :(, also you can read about this in more detail here
class LinearLayoutManagerWithAccurateOffset(context: Context?) : LinearLayoutManager(context) {
// map of child adapter position to its height.
private val childSizesMap = mutableMapOf<Int, Int>()
override fun onLayoutCompleted(state: RecyclerView.State?) {
super.onLayoutCompleted(state)
for (i in 0 until childCount) {
val child = getChildAt(i)
childSizesMap[getPosition(child)] = child.height
}
}
override fun computeVerticalScrollOffset(state: RecyclerView.State?): Int {
if (childCount == 0) {
return 0
}
val firstChildPosition = findFirstVisibleItemPosition()
val firstChild = findViewByPosition(firstChildPosition)
var scrolledY: Int = -firstChild.y.toInt()
for (i in 0 until firstChildPosition) {
scrolledY += childSizesMap[i] ?: 0
}
return scrolledY
}
}
UPDATE
Be aware that this does not support saving state, so onConfigurationChange/recovering from process death, the state will get out of sync and the scrolling will look weird, the way to fix this is to store the childSizesMap
in bundle by overriding onSaveInstanceState
The issue ended up being the fact that I had a very large invisible item in my list followed by a bunch of smaller items. It turns out that LinearLayoutManager.computeVerticalScrollOffset() takes average row height into account when calculating. This caused an issue for me since that large item at the top was throwing off the average height of the rows. I ended up solving this by adding a few smaller invisible items on top instead of one large one to keep the average row height accurate.
I hope this helps anyone who faces a similar issue!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With