flexbox-layout
flexbox-layout copied to clipboard
FlexboxLayoutManager stops before reaching the bottom when flicking fast
- [x] I have searched existing issues and confirmed this is not a duplicate
Issues and steps to reproduce
Hello.
When flicking fast enough a FlexboxLayoutManager to reach the bottom, the scroll sometimes stops before reaching the bottom. See the video:
We can see a first overscroll, just below the "My labels" label, yet there are still items to show below. When flicking a second time, it properly reaches the bottom.
This is an issue I have reproduced a few times in the past (not necessarily on this specific RecyclerView). Reproducing it depends on various factors (it essentially depends on how you flick). Basically, it is easier to reproduce on a FlexboxLayoutManager with a lot of items, in ROW mode. The flick should be fast but small.
It looks like in some cases, the FlexboxLayoutManager doesn't have time to inflate the views fast enough, so the scroll stops before the FlexboxLayoutManager manages to inflate the next views. My items are actually not very wide, nor very high, so multiple of them can fit on the same row (which means that the FlexboxLayoutManager has to inflate more of them to follow the scroll speed). This item size could be a relevant factor in reproducing the issue.
Expected behavior
When flicking through a FlexboxLayoutManager, it should never stop with an overscroll before reaching the very bottom. We should always be able to reach the bottom with a single flick (assuming that the flick is powerful enough of course).
Version of the flexbox library
2.0.1
Link to code
Not much relevant code to give; the RecyclerView from the GIF is initialized like so:
recyclerView.layoutManager = FlexboxLayoutManager(activity).apply {
alignItems = AlignItems.CENTER
justifyContent = JustifyContent.FLEX_START
}
FYI the RV is not inside a NestedScrollView or any other scrolling parent (so this is probably unrelated to #420 ).
Possible leads?
As a matter of fact, when debugging the issue, I stumbled upon this block of code, and found it could be a reason for the FlexboxLayoutManager not inflating the views fast enough:
FlexboxHelper.java
if (sumCrossSize > needsCalcAmount && reachedToIndex) {
// Stop the calculation if the sum of cross size calculated reached to the point
// beyond the needsCalcAmount value to avoid unneeded calculation in a
// RecyclerView.
// To be precise, the decoration length may be added to the sumCrossSize,
// but we omit adding the decoration length because even without the decorator
// length, it's guaranteed that calculation is done at least beyond the
// needsCalcAmount
break;
}
And indeed, after forking the project and commenting these lines, I was surprised to see I couldn't reproduce the issue!
I thought this was a happy coincidence that @phcannessondkt suggested in #420 (PR #554) to remove these lines as well, to fix an (apparently) unrelated issue.
Don't hesitate to ask for more info. Thanks!
More info: removing the lines in FlexboxHelper.java
works because all the items are inflated immediately, which defeats the whole purpose of the RecyclerView. This doesn't look like a solution.
Also, I enabled FlexboxLayoutManager
's logs, and this is exactly what happens when I flick and the RecyclerView stops before the end:
D/FlexboxLayoutManager: computeVerticalScrollOffset: 0
E/D: [onBindViewHolder]
E/D: [onBindViewHolder]
D/FlexboxLayoutManager: computeVerticalScrollOffset: 258
D/FlexboxLayoutManager: computeVerticalScrollRange: 3864
D/FlexboxLayoutManager: computeVerticalScrollExtent: 1414
D/FlexboxLayoutManager: computeVerticalScrollOffset: 258
D/FlexboxLayoutManager: computeVerticalScrollRange: 3864
D/FlexboxLayoutManager: computeVerticalScrollExtent: 1414
The scroll offset goes directly from 0 to 258 (i.e. the offset just under "My labels"), because I flicked fast. But since there are no intermediary scroll offset values, could it cause FlexboxLayoutManager
to not detect it has to inflate new views?
I have a similar issue when I scroll horizontally. Only facing an issue when I put the recycler view in the item with a nested scroll view.
item.xml <RelativeLayout android:id="@+id/relContainer" android:layout_width="@dimen/_93sdp" android:layout_height="wrap_content" android:orientation="vertical">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/_2sdp"
app:cardBackgroundColor="@color/color_white"
app:cardCornerRadius="@dimen/_2sdp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:id="@+id/linTopContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/color_196ed9"
android:baselineAligned="false"
android:orientation="horizontal"
android:padding="@dimen/_5sdp">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical">
<com.google.android.material.textview.MaterialTextView
android:id="@+id/txtOrderNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:fontFamily="@font/robotobold"
android:singleLine="true"
android:textColor="@color/color_white"
android:textSize="@dimen/_4ssp"
tools:text="#123" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/txtItemDetailStationName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/robotoregular"
android:textColor="@color/color_white"
android:textSize="@dimen/_4ssp"
tools:text="Curbside - Danny D." />
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Chronometer
android:id="@+id/chrnOrderTiming"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:fontFamily="@font/robotoregular"
android:textColor="@color/color_white"
android:textSize="@dimen/_5ssp" />
<com.google.android.material.imageview.ShapeableImageView
android:id="@+id/imgOrderTick"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/_3sdp"
android:src="@drawable/ic_tick_icon"
android:visibility="gone" />
</RelativeLayout>
</LinearLayout>
<androidx.core.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/linTopContainer"
android:background="@color/color_white"
android:fillViewport="true"
android:scrollbars="none">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvOrderListStationOrderItems"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:itemCount="5"
tools:listitem="@layout/item_station_order_item" />
</androidx.core.widget.NestedScrollView>
</RelativeLayout>
</androidx.cardview.widget.CardView>
My problem is that scrolling is very sluggish and jittery (with roughly 4x20 = 80 views on the screen), and often with abrupt stops. I thought removing the lines of code as suggested may help with my situation, but no. Without those lines, the problem is worse, much slower and with blank rows on the screen.