RecyclerView LinearLayoutManager computeVerticalScrollOffset () does not return the correct value

I work with RecyclerView and the corresponding LinearLayoutManager. I added some custom scroll functions to the part of my application where I would translate the custom header object along with the RecyclerView list modeled after this project: https://github.com/boxme/ParallaxHeaderViewPager (which uses ListView instead of RecyclerView).

I ran into a strange problem. It scrolls for a while, but then it jumps a few hundred pixels. I added log operators to see the offset calculated by LinearLayoutManager.computeVerticalScrollOffset (), when I scrolled it confidently enough, the offset randomly jumps from 320 to 1200, after which it will continue to calculate the offset accordingly from this point.

See my answer below on how I solved this!

+10
source share
2 answers

The problem was that I had a very large invisible item on my list, followed by a bunch of small items. It turns out that LinearLayoutManager.computeVerticalScrollOffset () takes into account the average row height when calculating. This caused me a problem, as this large element at the top discarded the average row height. I decided to solve this by adding a few smaller invisible objects on top, rather than one large one, to maintain the average line height.

I hope this helps anyone who encounters a similar problem!

+8
source

Thanks to this topic, we finally found a way to solve this problem by posting the code below for the redefined LinearLayoutManager, sorry for its kotlin :(, you can also read about it 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 } } 
+3
source

Source: https://habr.com/ru/post/987617/


All Articles