From de2e8a57ee7cd49ff2de0014adb0e7950429991d Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Sun, 5 Nov 2023 23:59:41 +0100 Subject: [PATCH 1/6] :art: Reformat code --- .../library/LoopingLayoutManager.kt | 140 +++++++++++------- 1 file changed, 83 insertions(+), 57 deletions(-) diff --git a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt index 179e40f..94d9ceb 100644 --- a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt +++ b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt @@ -61,19 +61,21 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr */ public val layoutWidth: Int get() = width - paddingLeft - paddingRight + /** * The height of the layout - not the recycler. * AKA the height of the recycler, minus the padding on the top and bottom. */ public val layoutHeight: Int get() = height - paddingTop - paddingBottom - + /** Describes the adapter index of the view in the top/left -most position. */ public var topLeftIndex = 0 - private set + private set + /** Describes the adapter index of the view in the bottom/right -most position. */ public var bottomRightIndex = 0 - private set + private set /** * The adapter index of the view at the edge of the layout where the 0th item was @@ -93,8 +95,9 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * When the layout manager needs to scroll to a position (via smooth scrolling) it needs some * method to decide which movement direction to scroll in. This variable stores that method. */ - public var smoothScrollDirectionDecider: (Int, LoopingLayoutManager, Int) -> Int = ::defaultDecider - + public var smoothScrollDirectionDecider: (Int, LoopingLayoutManager, Int) -> Int = + ::defaultDecider + /** * Creates a LoopingLayout manager with the given orientation and reverse layout option. * @param context Current context, will be used to access resources. @@ -102,7 +105,11 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * @param reverseLayout When set to true, lays out items in the opposite direction from default. */ @JvmOverloads - public constructor(context: Context, orientation: Int = VERTICAL, reverseLayout: Boolean = false) { + public constructor( + context: Context, + orientation: Int = VERTICAL, + reverseLayout: Boolean = false + ) { this.orientation = orientation this.reverseLayout = reverseLayout } @@ -166,8 +173,10 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr } public override fun generateDefaultLayoutParams(): LayoutParams { - return LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT) + return LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) } public override fun onSaveInstanceState(): Parcelable? { @@ -178,8 +187,9 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr null } else { LayoutRequest( - anchorIndex = getInitialIndex(direction), - scrollOffset = getInitialItem(direction).hiddenSize) + anchorIndex = getInitialIndex(direction), + scrollOffset = getInitialItem(direction).hiddenSize + ) } } @@ -189,7 +199,10 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr } } - public override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: RecyclerView.State) { + public override fun onLayoutChildren( + recycler: RecyclerView.Recycler, + state: RecyclerView.State + ) { layoutRequest.initialize(this, state) detachAndScrapAttachedViews(recycler) @@ -207,10 +220,12 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr val view = createViewForIndex(index, movementDir, recycler) val item = getItemForView(movementDir, view) var layoutRect = getNonScrollingEdges(view) - layoutRect = prevItem?.getPositionOfItemFollowingSelf(item, layoutRect) ?: - item.getPositionOfSelfAsFirst(layoutRect, layoutRequest.scrollOffset) - layoutDecorated(view, layoutRect.left, layoutRect.top, - layoutRect.right, layoutRect.bottom) + layoutRect = prevItem?.getPositionOfItemFollowingSelf(item, layoutRect) + ?: item.getPositionOfSelfAsFirst(layoutRect, layoutRequest.scrollOffset) + layoutDecorated( + view, layoutRect.left, layoutRect.top, + layoutRect.right, layoutRect.bottom + ) index = stepIndex(index, movementDir, state, false) sizeFilled += item.size @@ -290,23 +305,27 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr val newItem = getItemForView(movementDir, newView) var layoutRect = getNonScrollingEdges(newView) layoutRect = selectedItem.getPositionOfItemFollowingSelf(newItem, layoutRect) - layoutDecorated(newView, layoutRect.left, layoutRect.top, - layoutRect.right, layoutRect.bottom) + layoutDecorated( + newView, layoutRect.left, layoutRect.top, + layoutRect.right, layoutRect.bottom + ) selectedItem = newItem } } // The amount of extra (i.e not visible) space currently covered by views. var viewSpace = selectedItem.hiddenSize - while(viewSpace < extraLayoutSpace) { + while (viewSpace < extraLayoutSpace) { // We don't want the topLeftIndex or bottomRightIndex to reflect non-visible views. index = stepIndex(index, movementDir, state, updateIndex = false) val newView = createViewForIndex(index, movementDir, recycler) val newItem = getItemForView(movementDir, newView) var layoutRect = getNonScrollingEdges(newView) layoutRect = selectedItem.getPositionOfItemFollowingSelf(newItem, layoutRect) - layoutDecorated(newView, layoutRect.left, layoutRect.top, - layoutRect.right, layoutRect.bottom) + layoutDecorated( + newView, layoutRect.left, layoutRect.top, + layoutRect.right, layoutRect.bottom + ) selectedItem = newItem viewSpace += selectedItem.size } @@ -414,10 +433,10 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * newest view. This functionality can be disabled by passing "false" to the [updateIndex] parameter. */ private fun stepIndex( - index: Int, - movementDir: Int, - state: RecyclerView.State, - updateIndex: Boolean = true + index: Int, + movementDir: Int, + state: RecyclerView.State, + updateIndex: Boolean = true ): Int { val adapterDirection = getAdapterDirectionFromMovementDirection(movementDir) val count = state.itemCount @@ -435,7 +454,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr } isTowardsTopLeft && isTowardsLowerIndices -> { newIndex = index.loopedDecrement(count) - if (updateIndex) topLeftIndex = newIndex + if (updateIndex) topLeftIndex = newIndex } isTowardsBottomRight && isTowardsHigherIndices -> { newIndex = index.loopedIncrement(count) @@ -505,7 +524,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr val range = if (movementDir == TOWARDS_TOP_LEFT) { 0 until childCount } else { - childCount-1 downTo 0 + childCount - 1 downTo 0 } // Ignore hidden views at the start of the range. @@ -517,7 +536,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr foundVisibleView = true } distanceFromStart++ - } else if (foundVisibleView){ + } else if (foundVisibleView) { viewsToRemove.add(i) } } @@ -528,7 +547,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr removeAndRecycleViewAt(i, recycler) } - if (viewsToRemove.count() == 0) { + if (viewsToRemove.isEmpty()) { // If we didn't find anything that needed to be disposed, no indices need to be updated. return } @@ -779,8 +798,8 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * position. */ public fun findViewByPosition( - adapterIndex: Int, - strategy: (targetIndex: Int, layoutManager: LoopingLayoutManager) -> View? + adapterIndex: Int, + strategy: (targetIndex: Int, layoutManager: LoopingLayoutManager) -> View? ): View? { return strategy(adapterIndex, this) } @@ -808,7 +827,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr var lowestIndex = Int.MAX_VALUE; for (i in 0 until childCount) { val view = getChildAt(i); - if (view != null && getPosition(view) < lowestIndex && viewIsVisible(view)) { + if (view != null && getPosition(view) < lowestIndex && viewIsVisible(view)) { lowestIndex = getPosition(view) } } @@ -822,7 +841,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr var lowestIndex = Int.MAX_VALUE; for (i in 0 until childCount) { val view = getChildAt(i); - if (view != null && getPosition(view) < lowestIndex && viewIsFullyVisible(view)) { + if (view != null && getPosition(view) < lowestIndex && viewIsFullyVisible(view)) { lowestIndex = getPosition(view) } } @@ -837,7 +856,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr var highestIndex = 0; for (i in 0 until childCount) { val view = getChildAt(i) - if (view != null && getPosition(view) > highestIndex && viewIsVisible(view)) { + if (view != null && getPosition(view) > highestIndex && viewIsVisible(view)) { highestIndex = getPosition(view) } } @@ -851,7 +870,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr var highestIndex = 0; for (i in 0 until childCount) { val view = getChildAt(i) - if (view != null && getPosition(view) > highestIndex && viewIsFullyVisible(view)) { + if (view != null && getPosition(view) > highestIndex && viewIsFullyVisible(view)) { highestIndex = getPosition(view) } } @@ -882,12 +901,12 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * Note that this change will not be reflected until the next layout call. */ public fun scrollToPosition( - adapterIndex: Int, - strategy: ( - targetIndex: Int, - layoutManager: LoopingLayoutManager, - itemCount: Int - ) -> Int + adapterIndex: Int, + strategy: ( + targetIndex: Int, + layoutManager: LoopingLayoutManager, + itemCount: Int + ) -> Int ) { if (viewWithIndexIsFullyVisible(adapterIndex)) return layoutRequest = LayoutRequest(anchorIndex = adapterIndex, scrollStrategy = strategy) @@ -909,9 +928,9 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr } public override fun smoothScrollToPosition( - recyclerView: RecyclerView, - state: RecyclerView.State, - position: Int + recyclerView: RecyclerView, + state: RecyclerView.State, + position: Int ) { val loopingSmoothScroller = LoopingSmoothScroller(recyclerView.context, state) loopingSmoothScroller.targetPosition = position @@ -1093,8 +1112,8 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * a scroll vector. */ private inner class LoopingSmoothScroller( - val context: Context, - val state: RecyclerView.State + val context: Context, + val state: RecyclerView.State ) : LinearSmoothScroller(context) { /** @@ -1184,12 +1203,12 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr } public constructor( - anchorIndex: Int = RecyclerView.NO_POSITION, - scrollOffset: Int = 0, - adapterDirection: Int = TOWARDS_LOWER_INDICES, - scrollStrategy: ((Int, LoopingLayoutManager, Int) -> Int)? = null, - layoutManager: LoopingLayoutManager? = null, - state: RecyclerView.State? = null + anchorIndex: Int = RecyclerView.NO_POSITION, + scrollOffset: Int = 0, + adapterDirection: Int = TOWARDS_LOWER_INDICES, + scrollStrategy: ((Int, LoopingLayoutManager, Int) -> Int)? = null, + layoutManager: LoopingLayoutManager? = null, + state: RecyclerView.State? = null ) : this() { this.anchorIndex = anchorIndex this.scrollOffset = scrollOffset @@ -1199,8 +1218,9 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr if (layoutManager != null && state != null) initialize(layoutManager, state) if (!hasBeenInitialized - && anchorIndex != RecyclerView.NO_POSITION - && scrollStrategy == null) { + && anchorIndex != RecyclerView.NO_POSITION + && scrollStrategy == null + ) { hasBeenInitialized = true } } @@ -1213,8 +1233,10 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr hasBeenInitialized = true // If this is executing a scrollTo, the anchorIndex will be set, but the // adapterDirection still needs to be decided. - adapterDirection = scrollStrategy?.invoke(anchorIndex, layoutManager, state.itemCount)?.let { - layoutManager.getAdapterDirectionFromMovementDirection(it) } + adapterDirection = + scrollStrategy?.invoke(anchorIndex, layoutManager, state.itemCount)?.let { + layoutManager.getAdapterDirectionFromMovementDirection(it) + } ?: adapterDirection // If this is an adapter data update, the adapterDirection will be set but the // anchorIndex and scrollOffset still need to be decided. @@ -1222,7 +1244,8 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr if (layoutManager.childCount == 0) { anchorIndex = 0 } else { - val direction = layoutManager.getMovementDirectionFromAdapterDirection(adapterDirection) + val direction = + layoutManager.getMovementDirectionFromAdapterDirection(adapterDirection) anchorIndex = layoutManager.getInitialIndex(direction) scrollOffset = layoutManager.getInitialItem(direction).hiddenSize } @@ -1276,6 +1299,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * top/left. */ public const val TOWARDS_TOP_LEFT = -1 + /** * Describes the user scrolling towards the bottom/right of the screen. NOTE: this does * *not* describe the direction views are moving in. The user is trying to see new views @@ -1289,6 +1313,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * indices. */ public const val TOWARDS_LOWER_INDICES = -1 + /** * Describes the direction we need to traverse view indices in to get to larger adapter indices. * In this case we need to traverse the views forwards (0 -> Max) to get to higher adapter @@ -1302,6 +1327,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * */ private const val SCROLL_OFFSET = 100 + /** * A constant returned by the [computeScrollRange] function so that accessibility knows * the layout is always scrollable. @@ -1309,4 +1335,4 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr private const val SCROLL_RANGE = 200 } -} +} \ No newline at end of file From 78d00549a473135144d6ba61a890892b7bac2a48 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Thu, 31 Jul 2025 16:21:40 +0200 Subject: [PATCH 2/6] Make LoopingSmoothScroller public and open to allow customization --- .../loopinglayout/library/LoopingLayoutManager.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt index 94d9ceb..61d1309 100644 --- a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt +++ b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt @@ -1111,7 +1111,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * 2) The layout manager must be given the state.itemCount to properly calculate * a scroll vector. */ - private inner class LoopingSmoothScroller( + open class LoopingSmoothScroller( val context: Context, val state: RecyclerView.State ) : LinearSmoothScroller(context) { @@ -1335,4 +1335,4 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr private const val SCROLL_RANGE = 200 } -} \ No newline at end of file +} From fb3cb1f1d12b14d27d1fa9f14288a31e3f6a9703 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Thu, 31 Jul 2025 16:31:06 +0200 Subject: [PATCH 3/6] Don't crash if wrong LayoutManager is used This makes it consistent with the computeScrollVectorForPosition function. --- .../library/LoopingLayoutManager.kt | 35 ++++++++++++++----- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt index 61d1309..bbe73e8 100644 --- a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt +++ b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt @@ -1122,10 +1122,15 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * deceleration. */ public override fun onStart() { - // Based on the Material Design Guidelines, 500 ms should be plenty of time to decelerate. - val rate = calculateSpeedPerPixel(context.resources.displayMetrics) // MS/Pixel - val time = 500 // MS. - (layoutManager as LoopingLayoutManager).extraLayoutSpace = (rate * time).toInt() + when (val layoutManager = layoutManager) { + is LoopingLayoutManager -> { + // Based on the Material Design Guidelines, 500 ms should be plenty of time to decelerate. + val rate = calculateSpeedPerPixel(context.resources.displayMetrics) // MS/Pixel + val time = 500 // MS. + layoutManager.extraLayoutSpace = (rate * time).toInt() + } + else -> logLayoutManagerWarning() + } } /** @@ -1133,16 +1138,28 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * to lay out views the user can't see. */ public override fun onStop() { - (layoutManager as LoopingLayoutManager).extraLayoutSpace = 0 + when (val layoutManager = layoutManager) { + is LoopingLayoutManager -> { + layoutManager.extraLayoutSpace = 0 + } + else -> logLayoutManagerWarning() + } } public override fun computeScrollVectorForPosition(targetPosition: Int): PointF? { - val layoutManager = layoutManager // Enables smart cast. - if (layoutManager is LoopingLayoutManager) { - return layoutManager.computeScrollVectorForPosition(targetPosition, state.itemCount) + return when (val layoutManager = layoutManager) { + is LoopingLayoutManager -> { + layoutManager.computeScrollVectorForPosition(targetPosition, state.itemCount) + } + else -> { + logLayoutManagerWarning() + null + } } + } + + private fun logLayoutManagerWarning() { Log.w(TAG, "A LoopingSmoothScroller should only be attached to a LoopingLayoutManager.") - return null } } From b60fb967d7135cc241666abbf02b73e6ee6e5d33 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Thu, 31 Jul 2025 16:32:40 +0200 Subject: [PATCH 4/6] LoopingSmoothScroller: call super functions While onStart is empty, onStop handles some cleanup in LinearSmoothScroller and should thus call the super function. --- .../bekawestberg/loopinglayout/library/LoopingLayoutManager.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt index bbe73e8..3c6c8b6 100644 --- a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt +++ b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt @@ -1122,6 +1122,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * deceleration. */ public override fun onStart() { + super.onStart() when (val layoutManager = layoutManager) { is LoopingLayoutManager -> { // Based on the Material Design Guidelines, 500 ms should be plenty of time to decelerate. @@ -1138,6 +1139,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr * to lay out views the user can't see. */ public override fun onStop() { + super.onStop() when (val layoutManager = layoutManager) { is LoopingLayoutManager -> { layoutManager.extraLayoutSpace = 0 From 3362431bb9842ea548be4bd81383cff3653b2bb2 Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Thu, 31 Jul 2025 16:34:36 +0200 Subject: [PATCH 5/6] Fix incorrect extraLayoutSpace calculation The unit returned by calculateSpeedPerPixel is ms/pixel, so to get a pixel distance one has to divide the target time by the "speed". --- .../loopinglayout/library/LoopingLayoutManager.kt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt index 3c6c8b6..4331dc7 100644 --- a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt +++ b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt @@ -1126,9 +1126,9 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr when (val layoutManager = layoutManager) { is LoopingLayoutManager -> { // Based on the Material Design Guidelines, 500 ms should be plenty of time to decelerate. - val rate = calculateSpeedPerPixel(context.resources.displayMetrics) // MS/Pixel - val time = 500 // MS. - layoutManager.extraLayoutSpace = (rate * time).toInt() + val rate = calculateSpeedPerPixel(context.resources.displayMetrics) // ms/pixel + val time = 500 // ms + layoutManager.extraLayoutSpace = (time / rate).toInt() } else -> logLayoutManagerWarning() } From 59b163063dcb174b66d96fb6fbca1b1e1fd5b19b Mon Sep 17 00:00:00 2001 From: Maxr1998 Date: Thu, 31 Jul 2025 16:37:14 +0200 Subject: [PATCH 6/6] Set extraLayoutSpace to > 0 to layout at least one more view Otherwise, the layout manager would only layout the current view where item width = recycler width, which in turn can cause weird effects when smooth scrolling: In a production app, we observed the smooth scroller shooting past the target which was the immediate next view, taking an extra round and looping back to the actual target. Setting the extraLayoutSpace to 1 fixes this issue. --- .../bekawestberg/loopinglayout/library/LoopingLayoutManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt index 4331dc7..bf54595 100644 --- a/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt +++ b/library/src/main/java/com/bekawestberg/loopinglayout/library/LoopingLayoutManager.kt @@ -1142,7 +1142,7 @@ open class LoopingLayoutManager : LayoutManager, RecyclerView.SmoothScroller.Scr super.onStop() when (val layoutManager = layoutManager) { is LoopingLayoutManager -> { - layoutManager.extraLayoutSpace = 0 + layoutManager.extraLayoutSpace = 1 } else -> logLayoutManagerWarning() }