aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrey Kulikov <andreykulikov@google.com>2023-05-26 18:21:19 +0100
committerAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2023-06-06 03:23:16 +0000
commit09007340b37454fbadc12e7122ff6d7ac1d892ff (patch)
treef9dff60cedbb2b77aeb751d888f0d48f99dfc2b0
parentb76b96455de6f7a21c22983d33ca78df07aa7a37 (diff)
downloadsupport-09007340b37454fbadc12e7122ff6d7ac1d892ff.tar.gz
Decrease amount of allocations in lazy layouts
Tested on LazyListScrollingBenchmark.scrollProgrammatically_noNewItems Before: 1,433,275 ns 255 allocs After: 1,370,767 ns 191 allocs Test: existing tests and benchmarks (cherry picked from https://android-review.googlesource.com/q/commit:6aee2ac85a7f34eb7d583e31e2add6cb365c215b) Merged-In: I57ce5e07812a5d7b9c336010794b4fda450efc24 Change-Id: I57ce5e07812a5d7b9c336010794b4fda450efc24
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt54
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt16
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt26
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt22
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItem.kt97
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt12
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt31
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt14
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt36
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt62
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt26
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt102
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt12
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt5
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt16
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt31
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt14
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt28
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt147
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt3
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridScrollPosition.kt46
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt9
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/MeasuredPage.kt115
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt32
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt25
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt4
-rw-r--r--compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PositionedPage.kt44
27 files changed, 456 insertions, 573 deletions
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
index 05949c78d1d..c7be8b629d2 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyList.kt
@@ -38,6 +38,7 @@ import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.IntOffset
@@ -216,8 +217,6 @@ private fun rememberLazyListMeasurePolicy(
val contentConstraints =
containerConstraints.offset(-totalHorizontalPadding, -totalVerticalPadding)
- state.updateScrollPositionIfTheFirstItemWasMoved(itemProvider)
-
// Update the state's cached Density
state.density = this
@@ -254,37 +253,46 @@ private fun rememberLazyListMeasurePolicy(
)
}
- val measuredItemProvider = LazyListMeasuredItemProvider(
+ val measuredItemProvider = object : LazyListMeasuredItemProvider(
contentConstraints,
isVertical,
itemProvider,
this
- ) { index, key, contentType, placeables ->
- // we add spaceBetweenItems as an extra spacing for all items apart from the last one so
- // the lazy list measuring logic will take it into account.
- val spacing = if (index == itemsCount - 1) 0 else spaceBetweenItems
- LazyListMeasuredItem(
- index = index,
- placeables = placeables,
- isVertical = isVertical,
- horizontalAlignment = horizontalAlignment,
- verticalAlignment = verticalAlignment,
- layoutDirection = layoutDirection,
- reverseLayout = reverseLayout,
- beforeContentPadding = beforeContentPadding,
- afterContentPadding = afterContentPadding,
- spacing = spacing,
- visualOffset = visualItemOffset,
- key = key,
- contentType = contentType
- )
+ ) {
+ override fun createItem(
+ index: Int,
+ key: Any,
+ contentType: Any?,
+ placeables: List<Placeable>
+ ): LazyListMeasuredItem {
+ // we add spaceBetweenItems as an extra spacing for all items apart from the last one so
+ // the lazy list measuring logic will take it into account.
+ val spacing = if (index == itemsCount - 1) 0 else spaceBetweenItems
+ return LazyListMeasuredItem(
+ index = index,
+ placeables = placeables,
+ isVertical = isVertical,
+ horizontalAlignment = horizontalAlignment,
+ verticalAlignment = verticalAlignment,
+ layoutDirection = layoutDirection,
+ reverseLayout = reverseLayout,
+ beforeContentPadding = beforeContentPadding,
+ afterContentPadding = afterContentPadding,
+ spacing = spacing,
+ visualOffset = visualItemOffset,
+ key = key,
+ contentType = contentType
+ )
+ }
}
state.premeasureConstraints = measuredItemProvider.childConstraints
val firstVisibleItemIndex: Int
val firstVisibleScrollOffset: Int
Snapshot.withoutReadObservation {
- firstVisibleItemIndex = state.firstVisibleItemIndex
+ firstVisibleItemIndex = state.updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider, state.firstVisibleItemIndex
+ )
firstVisibleScrollOffset = state.firstVisibleItemScrollOffset
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
index 978d3a81489..8cf55e5f531 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListHeaders.kt
@@ -28,13 +28,13 @@ import androidx.compose.ui.util.fastForEachIndexed
* @param beforeContentPadding the padding before the first item in the list
*/
internal fun findOrComposeLazyListHeader(
- composedVisibleItems: MutableList<LazyListPositionedItem>,
+ composedVisibleItems: MutableList<LazyListMeasuredItem>,
itemProvider: LazyListMeasuredItemProvider,
headerIndexes: List<Int>,
beforeContentPadding: Int,
layoutWidth: Int,
layoutHeight: Int,
-): LazyListPositionedItem? {
+): LazyListMeasuredItem? {
var currentHeaderOffset: Int = Int.MIN_VALUE
var nextHeaderOffset: Int = Int.MIN_VALUE
@@ -83,11 +83,11 @@ internal fun findOrComposeLazyListHeader(
headerOffset = minOf(headerOffset, nextHeaderOffset - measuredHeaderItem.size)
}
- return measuredHeaderItem.position(headerOffset, layoutWidth, layoutHeight).also {
- if (indexInComposedVisibleItems != -1) {
- composedVisibleItems[indexInComposedVisibleItems] = it
- } else {
- composedVisibleItems.add(0, it)
- }
+ measuredHeaderItem.position(headerOffset, layoutWidth, layoutHeight)
+ if (indexInComposedVisibleItems != -1) {
+ composedVisibleItems[indexInComposedVisibleItems] = measuredHeaderItem
+ } else {
+ composedVisibleItems.add(0, measuredHeaderItem)
}
+ return measuredHeaderItem
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
index 497ba6d4cdc..ec0fb82e7e4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListItemPlacementAnimator.kt
@@ -40,8 +40,8 @@ internal class LazyListItemPlacementAnimator {
// stored to not allocate it every pass.
private val movingAwayKeys = LinkedHashSet<Any>()
- private val movingInFromStartBound = mutableListOf<LazyListPositionedItem>()
- private val movingInFromEndBound = mutableListOf<LazyListPositionedItem>()
+ private val movingInFromStartBound = mutableListOf<LazyListMeasuredItem>()
+ private val movingInFromEndBound = mutableListOf<LazyListMeasuredItem>()
private val movingAwayToStartBound = mutableListOf<LazyListMeasuredItem>()
private val movingAwayToEndBound = mutableListOf<LazyListMeasuredItem>()
@@ -54,7 +54,7 @@ internal class LazyListItemPlacementAnimator {
consumedScroll: Int,
layoutWidth: Int,
layoutHeight: Int,
- positionedItems: MutableList<LazyListPositionedItem>,
+ positionedItems: MutableList<LazyListMeasuredItem>,
itemProvider: LazyListMeasuredItemProvider,
isVertical: Boolean
) {
@@ -167,9 +167,9 @@ internal class LazyListItemPlacementAnimator {
accumulatedOffset += item.size
val mainAxisOffset = 0 - accumulatedOffset
- val positionedItem = item.position(mainAxisOffset, layoutWidth, layoutHeight)
- positionedItems.add(positionedItem)
- startAnimationsIfNeeded(positionedItem)
+ item.position(mainAxisOffset, layoutWidth, layoutHeight)
+ positionedItems.add(item)
+ startAnimationsIfNeeded(item)
}
accumulatedOffset = 0
movingAwayToEndBound.sortBy { keyIndexMap.getIndex(it.key) }
@@ -177,9 +177,9 @@ internal class LazyListItemPlacementAnimator {
val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
accumulatedOffset += item.size
- val positionedItem = item.position(mainAxisOffset, layoutWidth, layoutHeight)
- positionedItems.add(positionedItem)
- startAnimationsIfNeeded(positionedItem)
+ item.position(mainAxisOffset, layoutWidth, layoutHeight)
+ positionedItems.add(item)
+ startAnimationsIfNeeded(item)
}
movingInFromStartBound.clear()
@@ -200,7 +200,7 @@ internal class LazyListItemPlacementAnimator {
}
private fun initializeNode(
- item: LazyListPositionedItem,
+ item: LazyListMeasuredItem,
mainAxisOffset: Int
) {
val firstPlaceableOffset = item.getOffset(0)
@@ -219,7 +219,7 @@ internal class LazyListItemPlacementAnimator {
}
}
- private fun startAnimationsIfNeeded(item: LazyListPositionedItem) {
+ private fun startAnimationsIfNeeded(item: LazyListMeasuredItem) {
item.forEachNode { placeableIndex, node ->
val newTarget = item.getOffset(placeableIndex)
val currentTarget = node.rawOffset
@@ -234,13 +234,13 @@ internal class LazyListItemPlacementAnimator {
private val Any?.node get() = this as? LazyLayoutAnimateItemModifierNode
- private val LazyListPositionedItem.hasAnimations: Boolean
+ private val LazyListMeasuredItem.hasAnimations: Boolean
get() {
forEachNode { _, _ -> return true }
return false
}
- private inline fun LazyListPositionedItem.forEachNode(
+ private inline fun LazyListMeasuredItem.forEachNode(
block: (placeableIndex: Int, node: LazyLayoutAnimateItemModifierNode) -> Unit
) {
repeat(placeablesCount) { index ->
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
index c247a1dcf82..7b4d920828d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasure.kt
@@ -102,7 +102,7 @@ internal fun measureLazyList(
}
// this will contain all the MeasuredItems representing the visible items
- val visibleItems = mutableListOf<LazyListMeasuredItem>()
+ val visibleItems = ArrayDeque<LazyListMeasuredItem>()
// define min and max offsets
val minOffset = -beforeContentPadding + if (spaceBetweenItems < 0) spaceBetweenItems else 0
@@ -403,7 +403,7 @@ private fun calculateItemsOffsets(
horizontalArrangement: Arrangement.Horizontal?,
reverseLayout: Boolean,
density: Density,
-): MutableList<LazyListPositionedItem> {
+): MutableList<LazyListMeasuredItem> {
val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
val hasSpareSpace = finalMainAxisOffset < minOf(mainAxisLayoutSize, maxOffset)
if (hasSpareSpace) {
@@ -411,7 +411,7 @@ private fun calculateItemsOffsets(
}
val positionedItems =
- ArrayList<LazyListPositionedItem>(items.size + extraItemsBefore.size + extraItemsAfter.size)
+ ArrayList<LazyListMeasuredItem>(items.size + extraItemsBefore.size + extraItemsAfter.size)
if (hasSpareSpace) {
require(extraItemsBefore.isEmpty() && extraItemsAfter.isEmpty())
@@ -447,29 +447,29 @@ private fun calculateItemsOffsets(
} else {
absoluteOffset
}
- positionedItems.add(item.position(relativeOffset, layoutWidth, layoutHeight))
+ item.position(relativeOffset, layoutWidth, layoutHeight)
+ positionedItems.add(item)
}
} else {
var currentMainAxis = itemsScrollOffset
extraItemsBefore.fastForEach {
currentMainAxis -= it.sizeWithSpacings
- positionedItems.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+ it.position(currentMainAxis, layoutWidth, layoutHeight)
+ positionedItems.add(it)
}
currentMainAxis = itemsScrollOffset
items.fastForEach {
- positionedItems.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+ it.position(currentMainAxis, layoutWidth, layoutHeight)
+ positionedItems.add(it)
currentMainAxis += it.sizeWithSpacings
}
extraItemsAfter.fastForEach {
- positionedItems.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+ it.position(currentMainAxis, layoutWidth, layoutHeight)
+ positionedItems.add(it)
currentMainAxis += it.sizeWithSpacings
}
}
return positionedItems
}
-
-private val EmptyRange = Int.MIN_VALUE to Int.MIN_VALUE
-private val Int.notInEmptyRange
- get() = this != Int.MIN_VALUE \ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItem.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItem.kt
index 0c4b130f962..5fb7dc952f8 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItem.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItem.kt
@@ -23,15 +23,16 @@ import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
/**
* Represents one measured item of the lazy list. It can in fact consist of multiple placeables
* if the user emit multiple layout nodes in the item callback.
*/
internal class LazyListMeasuredItem @ExperimentalFoundationApi constructor(
- val index: Int,
+ override val index: Int,
private val placeables: List<Placeable>,
- private val isVertical: Boolean,
+ val isVertical: Boolean,
private val horizontalAlignment: Alignment.Horizontal?,
private val verticalAlignment: Alignment.Vertical?,
private val layoutDirection: LayoutDirection,
@@ -48,13 +49,16 @@ internal class LazyListMeasuredItem @ExperimentalFoundationApi constructor(
* value passed into the place() call.
*/
private val visualOffset: IntOffset,
- val key: Any,
- private val contentType: Any?
-) {
+ override val key: Any,
+ override val contentType: Any?
+) : LazyListItemInfo {
+ override var offset: Int = 0
+ private set
+
/**
* Sum of the main axis sizes of all the inner placeables.
*/
- val size: Int
+ override val size: Int
/**
* Sum of the main axis sizes of all the inner placeables and [spacing].
@@ -66,6 +70,14 @@ internal class LazyListMeasuredItem @ExperimentalFoundationApi constructor(
*/
val crossAxisSize: Int
+ private var mainAxisLayoutSize: Int = Unset
+ private var minMainAxisOffset: Int = 0
+ private var maxMainAxisOffset: Int = 0
+
+ // optimized for storing x and y offsets for each placeable one by one.
+ // array's size == placeables.size * 2, first we store x, then y.
+ private val placeableOffsets: IntArray
+
init {
var mainAxisSize = 0
var maxCrossAxis = 0
@@ -76,6 +88,7 @@ internal class LazyListMeasuredItem @ExperimentalFoundationApi constructor(
size = mainAxisSize
sizeWithSpacings = (size + spacing).coerceAtLeast(0)
crossAxisSize = maxCrossAxis
+ placeableOffsets = IntArray(placeables.size * 2)
}
val placeablesCount: Int get() = placeables.size
@@ -90,64 +103,37 @@ internal class LazyListMeasuredItem @ExperimentalFoundationApi constructor(
offset: Int,
layoutWidth: Int,
layoutHeight: Int
- ): LazyListPositionedItem {
- val wrappers = mutableListOf<LazyListPlaceableWrapper>()
- val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
+ ) {
+ this.offset = offset
+ mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
var mainAxisOffset = offset
- placeables.fastForEach {
- val placeableOffset = if (isVertical) {
- val x = requireNotNull(horizontalAlignment)
- .align(it.width, layoutWidth, layoutDirection)
- IntOffset(x, mainAxisOffset)
+ placeables.fastForEachIndexed { index, placeable ->
+ val indexInArray = index * 2
+ if (isVertical) {
+ placeableOffsets[indexInArray] = requireNotNull(horizontalAlignment)
+ .align(placeable.width, layoutWidth, layoutDirection)
+ placeableOffsets[indexInArray + 1] = mainAxisOffset
+ mainAxisOffset += placeable.height
} else {
- val y = requireNotNull(verticalAlignment).align(it.height, layoutHeight)
- IntOffset(mainAxisOffset, y)
+ placeableOffsets[indexInArray] = mainAxisOffset
+ placeableOffsets[indexInArray + 1] = requireNotNull(verticalAlignment)
+ .align(placeable.height, layoutHeight)
+ mainAxisOffset += placeable.width
}
- mainAxisOffset += if (isVertical) it.height else it.width
- wrappers.add(LazyListPlaceableWrapper(placeableOffset, it))
}
- return LazyListPositionedItem(
- offset = offset,
- index = this.index,
- key = key,
- size = size,
- minMainAxisOffset = -beforeContentPadding,
- maxMainAxisOffset = mainAxisLayoutSize + afterContentPadding,
- isVertical = isVertical,
- wrappers = wrappers,
- visualOffset = visualOffset,
- reverseLayout = reverseLayout,
- mainAxisLayoutSize = mainAxisLayoutSize,
- contentType = contentType
- )
+ minMainAxisOffset = -beforeContentPadding
+ maxMainAxisOffset = mainAxisLayoutSize + afterContentPadding
}
-}
-
-internal class LazyListPositionedItem(
- override val offset: Int,
- override val index: Int,
- override val key: Any,
- override val size: Int,
- private val minMainAxisOffset: Int,
- private val maxMainAxisOffset: Int,
- val isVertical: Boolean,
- private val wrappers: List<LazyListPlaceableWrapper>,
- private val visualOffset: IntOffset,
- private val reverseLayout: Boolean,
- private val mainAxisLayoutSize: Int,
- override val contentType: Any?
-) : LazyListItemInfo {
- val placeablesCount: Int get() = wrappers.size
-
- fun getOffset(index: Int) = wrappers[index].offset
- fun getParentData(index: Int) = wrappers[index].placeable.parentData
+ fun getOffset(index: Int) =
+ IntOffset(placeableOffsets[index * 2], placeableOffsets[index * 2 + 1])
fun place(
scope: Placeable.PlacementScope,
) = with(scope) {
+ require(mainAxisLayoutSize != Unset) { "position() should be called first" }
repeat(placeablesCount) { index ->
- val placeable = wrappers[index].placeable
+ val placeable = placeables[index]
val minOffset = minMainAxisOffset - placeable.mainAxisSize
val maxOffset = maxMainAxisOffset
var offset = getOffset(index)
@@ -182,7 +168,4 @@ internal class LazyListPositionedItem(
IntOffset(if (isVertical) x else mainAxisMap(x), if (isVertical) mainAxisMap(y) else y)
}
-internal class LazyListPlaceableWrapper(
- val offset: IntOffset,
- val placeable: Placeable
-)
+private const val Unset = Int.MIN_VALUE \ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt
index 77ec0760a3f..7c7a0ce5a32 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListMeasuredItemProvider.kt
@@ -26,12 +26,11 @@ import androidx.compose.ui.unit.Constraints
* Abstracts away the subcomposition from the measuring logic.
*/
@OptIn(ExperimentalFoundationApi::class)
-internal class LazyListMeasuredItemProvider @ExperimentalFoundationApi constructor(
+internal abstract class LazyListMeasuredItemProvider @ExperimentalFoundationApi constructor(
constraints: Constraints,
isVertical: Boolean,
private val itemProvider: LazyListItemProvider,
- private val measureScope: LazyLayoutMeasureScope,
- private val measuredItemFactory: MeasuredItemFactory
+ private val measureScope: LazyLayoutMeasureScope
) {
// the constraints we will measure child with. the main axis is not restricted
val childConstraints = Constraints(
@@ -47,7 +46,7 @@ internal class LazyListMeasuredItemProvider @ExperimentalFoundationApi construct
val key = keyIndexMap.getKey(index) ?: itemProvider.getKey(index)
val contentType = itemProvider.getContentType(index)
val placeables = measureScope.measure(index, childConstraints)
- return measuredItemFactory.createItem(index, key, contentType, placeables)
+ return createItem(index, key, contentType, placeables)
}
/**
@@ -55,11 +54,8 @@ internal class LazyListMeasuredItemProvider @ExperimentalFoundationApi construct
* the list as an optimization.
**/
val keyIndexMap: LazyLayoutKeyIndexMap = itemProvider.keyIndexMap
-}
-// This interface allows to avoid autoboxing on index param
-internal fun interface MeasuredItemFactory {
- fun createItem(
+ abstract fun createItem(
index: Int,
key: Any,
contentType: Any?,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt
index 16eb1c71ecb..744089fa22b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListScrollPosition.kt
@@ -21,7 +21,6 @@ import androidx.compose.foundation.lazy.layout.findIndexByKey
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.Snapshot
/**
* Contains the current scroll position represented by the first visible item index and the first
@@ -54,12 +53,8 @@ internal class LazyListScrollPosition(
val scrollOffset = measureResult.firstVisibleItemScrollOffset
check(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
- Snapshot.withoutReadObservation {
- update(
- measureResult.firstVisibleItem?.index ?: 0,
- scrollOffset
- )
- }
+ val firstIndex = measureResult.firstVisibleItem?.index ?: 0
+ update(firstIndex, scrollOffset)
}
}
@@ -88,22 +83,18 @@ internal class LazyListScrollPosition(
* as the first visible one even given that its index has been changed.
*/
@ExperimentalFoundationApi
- fun updateScrollPositionIfTheFirstItemWasMoved(itemProvider: LazyListItemProvider) {
- Snapshot.withoutReadObservation {
- update(
- itemProvider.findIndexByKey(lastKnownFirstItemKey, index),
- scrollOffset
- )
- }
+ fun updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider: LazyListItemProvider,
+ index: Int
+ ): Int {
+ val newIndex = itemProvider.findIndexByKey(lastKnownFirstItemKey, index)
+ this.index = newIndex
+ return newIndex
}
private fun update(index: Int, scrollOffset: Int) {
require(index >= 0f) { "Index should be non-negative ($index)" }
- if (index != this.index) {
- this.index = index
- }
- if (scrollOffset != this.scrollOffset) {
- this.scrollOffset = scrollOffset
- }
+ this.index = index
+ this.scrollOffset = scrollOffset
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
index 3bd36be0054..41384da64a3 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/LazyListState.kt
@@ -36,6 +36,7 @@ import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.layout.Remeasurement
import androidx.compose.ui.layout.RemeasurementModifier
import androidx.compose.ui.unit.Constraints
@@ -152,7 +153,7 @@ class LazyListState constructor(
/**
* Needed for [animateScrollToItem]. Updated on every measure.
*/
- internal var density: Density by mutableStateOf(Density(1f, 1f))
+ internal var density: Density = Density(1f, 1f)
/**
* The ScrollableController instance. We keep it as we need to call stopAnimation on it once
@@ -193,7 +194,7 @@ class LazyListState constructor(
* The [Remeasurement] object associated with our layout. It allows us to remeasure
* synchronously during scroll.
*/
- internal var remeasurement: Remeasurement? by mutableStateOf(null)
+ internal var remeasurement: Remeasurement? = null
private set
/**
@@ -218,7 +219,7 @@ class LazyListState constructor(
/**
* Constraints passed to the prefetcher for premeasuring the prefetched items.
*/
- internal var premeasureConstraints by mutableStateOf(Constraints())
+ internal var premeasureConstraints = Constraints()
/**
* Stores currently pinned items which are always composed.
@@ -401,9 +402,10 @@ class LazyListState constructor(
* items added or removed before our current first visible item and keep this item
* as the first visible one even given that its index has been changed.
*/
- internal fun updateScrollPositionIfTheFirstItemWasMoved(itemProvider: LazyListItemProvider) {
- scrollPosition.updateScrollPositionIfTheFirstItemWasMoved(itemProvider)
- }
+ internal fun updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider: LazyListItemProvider,
+ firstItemIndex: Int = Snapshot.withoutReadObservation { scrollPosition.index }
+ ): Int = scrollPosition.updateScrollPositionIfTheFirstItemWasMoved(itemProvider, firstItemIndex)
companion object {
/**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
index 4f84478fc76..ac446afbbac 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGrid.kt
@@ -37,6 +37,7 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.MeasureResult
+import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.platform.LocalLayoutDirection
import androidx.compose.ui.unit.Constraints
import androidx.compose.ui.unit.Density
@@ -215,8 +216,6 @@ private fun rememberLazyGridMeasurePolicy(
val contentConstraints =
containerConstraints.offset(-totalHorizontalPadding, -totalVerticalPadding)
- state.updateScrollPositionIfTheFirstItemWasMoved(itemProvider)
-
val spanLayoutProvider = itemProvider.spanLayoutProvider
val resolvedSlots = slots(containerConstraints)
val slotsPerLine = resolvedSlots.sizes.size
@@ -252,12 +251,19 @@ private fun rememberLazyGridMeasurePolicy(
)
}
- val measuredItemProvider = LazyGridMeasuredItemProvider(
+ val measuredItemProvider = object : LazyGridMeasuredItemProvider(
itemProvider,
this,
spaceBetweenLines
- ) { index, key, contentType, crossAxisSize, mainAxisSpacing, placeables ->
- LazyGridMeasuredItem(
+ ) {
+ override fun createItem(
+ index: Int,
+ key: Any,
+ contentType: Any?,
+ crossAxisSize: Int,
+ mainAxisSpacing: Int,
+ placeables: List<Placeable>
+ ) = LazyGridMeasuredItem(
index = index,
key = key,
isVertical = isVertical,
@@ -272,15 +278,20 @@ private fun rememberLazyGridMeasurePolicy(
contentType = contentType
)
}
- val measuredLineProvider = LazyGridMeasuredLineProvider(
+ val measuredLineProvider = object : LazyGridMeasuredLineProvider(
isVertical = isVertical,
slots = resolvedSlots,
gridItemsCount = itemsCount,
spaceBetweenLines = spaceBetweenLines,
measuredItemProvider = measuredItemProvider,
spanLayoutProvider = spanLayoutProvider
- ) { index, items, spans, mainAxisSpacing ->
- LazyGridMeasuredLine(
+ ) {
+ override fun createLine(
+ index: Int,
+ items: Array<LazyGridMeasuredItem>,
+ spans: List<GridItemSpan>,
+ mainAxisSpacing: Int
+ ) = LazyGridMeasuredLine(
index = index,
items = items,
spans = spans,
@@ -307,10 +318,11 @@ private fun rememberLazyGridMeasurePolicy(
val firstVisibleLineScrollOffset: Int
Snapshot.withoutReadObservation {
- if (state.firstVisibleItemIndex < itemsCount || itemsCount <= 0) {
- firstVisibleLineIndex = spanLayoutProvider.getLineIndexOfItem(
- state.firstVisibleItemIndex
- )
+ val index = state.updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider, state.firstVisibleItemIndex
+ )
+ if (index < itemsCount || itemsCount <= 0) {
+ firstVisibleLineIndex = spanLayoutProvider.getLineIndexOfItem(index)
firstVisibleLineScrollOffset = state.firstVisibleItemScrollOffset
} else {
// the data set has been updated and now we have less items that we were
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
index fbd96ee7e8a..918f2a2881f 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridItemPlacementAnimator.kt
@@ -41,8 +41,8 @@ internal class LazyGridItemPlacementAnimator {
// stored to not allocate it every pass.
private val movingAwayKeys = LinkedHashSet<Any>()
- private val movingInFromStartBound = mutableListOf<LazyGridPositionedItem>()
- private val movingInFromEndBound = mutableListOf<LazyGridPositionedItem>()
+ private val movingInFromStartBound = mutableListOf<LazyGridMeasuredItem>()
+ private val movingInFromEndBound = mutableListOf<LazyGridMeasuredItem>()
private val movingAwayToStartBound = mutableListOf<LazyGridMeasuredItem>()
private val movingAwayToEndBound = mutableListOf<LazyGridMeasuredItem>()
@@ -55,7 +55,7 @@ internal class LazyGridItemPlacementAnimator {
consumedScroll: Int,
layoutWidth: Int,
layoutHeight: Int,
- positionedItems: MutableList<LazyGridPositionedItem>,
+ positionedItems: MutableList<LazyGridMeasuredItem>,
itemProvider: LazyGridMeasuredItemProvider,
spanLayoutProvider: LazyGridSpanLayoutProvider,
isVertical: Boolean
@@ -91,7 +91,7 @@ internal class LazyGridItemPlacementAnimator {
// there is no state associated with this item yet
if (itemInfo == null) {
keyToItemInfoMap[item.key] =
- ItemInfo(item.getCrossAxisSize(), item.getCrossAxisOffset())
+ ItemInfo(item.crossAxisSize, item.crossAxisOffset)
val previousIndex = previousKeyToIndexMap.getIndex(item.key)
if (previousIndex != -1 && item.index != previousIndex) {
if (previousIndex < previousFirstVisibleIndex) {
@@ -112,8 +112,8 @@ internal class LazyGridItemPlacementAnimator {
it.rawOffset += scrollOffset
}
}
- itemInfo.crossAxisSize = item.getCrossAxisSize()
- itemInfo.crossAxisOffset = item.getCrossAxisOffset()
+ itemInfo.crossAxisSize = item.crossAxisSize
+ itemInfo.crossAxisOffset = item.crossAxisOffset
startAnimationsIfNeeded(item)
}
} else {
@@ -129,13 +129,13 @@ internal class LazyGridItemPlacementAnimator {
movingInFromStartBound.fastForEach { item ->
val line = if (isVertical) item.row else item.column
if (line != -1 && line == previousLine) {
- previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.getMainAxisSize())
+ previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.mainAxisSize)
} else {
accumulatedOffset += previousLineMainAxisSize
- previousLineMainAxisSize = item.getMainAxisSize()
+ previousLineMainAxisSize = item.mainAxisSize
previousLine = line
}
- val mainAxisOffset = 0 - accumulatedOffset - item.getMainAxisSize()
+ val mainAxisOffset = 0 - accumulatedOffset - item.mainAxisSize
initializeNode(item, mainAxisOffset)
startAnimationsIfNeeded(item)
}
@@ -146,10 +146,10 @@ internal class LazyGridItemPlacementAnimator {
movingInFromEndBound.fastForEach { item ->
val line = if (isVertical) item.row else item.column
if (line != -1 && line == previousLine) {
- previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.getMainAxisSize())
+ previousLineMainAxisSize = maxOf(previousLineMainAxisSize, item.mainAxisSize)
} else {
accumulatedOffset += previousLineMainAxisSize
- previousLineMainAxisSize = item.getMainAxisSize()
+ previousLineMainAxisSize = item.mainAxisSize
previousLine = line
}
val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
@@ -211,16 +211,14 @@ internal class LazyGridItemPlacementAnimator {
val itemInfo = keyToItemInfoMap.getValue(item.key)
- val positionedItem = item.position(
- mainAxisOffset,
- itemInfo.crossAxisOffset,
- layoutWidth,
- layoutHeight,
- LazyGridItemInfo.UnknownRow,
- LazyGridItemInfo.UnknownColumn
+ item.position(
+ mainAxisOffset = mainAxisOffset,
+ crossAxisOffset = itemInfo.crossAxisOffset,
+ layoutWidth = layoutWidth,
+ layoutHeight = layoutHeight
)
- positionedItems.add(positionedItem)
- startAnimationsIfNeeded(positionedItem)
+ positionedItems.add(item)
+ startAnimationsIfNeeded(item)
}
accumulatedOffset = 0
previousLine = -1
@@ -238,17 +236,15 @@ internal class LazyGridItemPlacementAnimator {
val mainAxisOffset = mainAxisLayoutSize + accumulatedOffset
val itemInfo = keyToItemInfoMap.getValue(item.key)
- val positionedItem = item.position(
- mainAxisOffset,
- itemInfo.crossAxisOffset,
- layoutWidth,
- layoutHeight,
- LazyGridItemInfo.UnknownRow,
- LazyGridItemInfo.UnknownColumn
+ item.position(
+ mainAxisOffset = mainAxisOffset,
+ crossAxisOffset = itemInfo.crossAxisOffset,
+ layoutWidth = layoutWidth,
+ layoutHeight = layoutHeight,
)
- positionedItems.add(positionedItem)
- startAnimationsIfNeeded(positionedItem)
+ positionedItems.add(item)
+ startAnimationsIfNeeded(item)
}
movingInFromStartBound.clear()
@@ -269,7 +265,7 @@ internal class LazyGridItemPlacementAnimator {
}
private fun initializeNode(
- item: LazyGridPositionedItem,
+ item: LazyGridMeasuredItem,
mainAxisOffset: Int
) {
val firstPlaceableOffset = item.offset
@@ -288,7 +284,7 @@ internal class LazyGridItemPlacementAnimator {
}
}
- private fun startAnimationsIfNeeded(item: LazyGridPositionedItem) {
+ private fun startAnimationsIfNeeded(item: LazyGridMeasuredItem) {
item.forEachNode { node ->
val newTarget = item.offset
val currentTarget = node.rawOffset
@@ -303,13 +299,13 @@ internal class LazyGridItemPlacementAnimator {
private val Any?.node get() = this as? LazyLayoutAnimateItemModifierNode
- private val LazyGridPositionedItem.hasAnimations: Boolean
+ private val LazyGridMeasuredItem.hasAnimations: Boolean
get() {
forEachNode { return true }
return false
}
- private inline fun LazyGridPositionedItem.forEachNode(
+ private inline fun LazyGridMeasuredItem.forEachNode(
block: (LazyLayoutAnimateItemModifierNode) -> Unit
) {
repeat(placeablesCount) { index ->
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
index d5fa3ecadbf..b9258895247 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasure.kt
@@ -98,7 +98,7 @@ internal fun measureLazyGrid(
}
// this will contain all the MeasuredItems representing the visible lines
- val visibleLines = mutableListOf<LazyGridMeasuredLine>()
+ val visibleLines = ArrayDeque<LazyGridMeasuredLine>()
// define min and max offsets
val minOffset = -beforeContentPadding + if (spaceBetweenLines < 0) spaceBetweenLines else 0
@@ -343,14 +343,14 @@ private fun calculateItemsOffsets(
horizontalArrangement: Arrangement.Horizontal?,
reverseLayout: Boolean,
density: Density,
-): MutableList<LazyGridPositionedItem> {
+): MutableList<LazyGridMeasuredItem> {
val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
val hasSpareSpace = finalMainAxisOffset < min(mainAxisLayoutSize, maxOffset)
if (hasSpareSpace) {
check(firstLineScrollOffset == 0)
}
- val positionedItems = ArrayList<LazyGridPositionedItem>(lines.fastSumBy { it.items.size })
+ val positionedItems = ArrayList<LazyGridMeasuredItem>(lines.fastSumBy { it.items.size })
if (hasSpareSpace) {
require(itemsBefore.isEmpty() && itemsAfter.isEmpty())
@@ -395,7 +395,8 @@ private fun calculateItemsOffsets(
itemsBefore.fastForEach {
currentMainAxis -= it.mainAxisSizeWithSpacings
- positionedItems.add(it.positionExtraItem(currentMainAxis, layoutWidth, layoutHeight))
+ it.position(currentMainAxis, 0, layoutWidth, layoutHeight)
+ positionedItems.add(it)
}
currentMainAxis = firstLineScrollOffset
@@ -405,23 +406,10 @@ private fun calculateItemsOffsets(
}
itemsAfter.fastForEach {
- positionedItems.add(it.positionExtraItem(currentMainAxis, layoutWidth, layoutHeight))
+ it.position(currentMainAxis, 0, layoutWidth, layoutHeight)
+ positionedItems.add(it)
currentMainAxis += it.mainAxisSizeWithSpacings
}
}
return positionedItems
}
-
-private fun LazyGridMeasuredItem.positionExtraItem(
- mainAxisOffset: Int,
- layoutWidth: Int,
- layoutHeight: Int
-): LazyGridPositionedItem =
- position(
- mainAxisOffset = mainAxisOffset,
- crossAxisOffset = 0,
- layoutWidth = layoutWidth,
- layoutHeight = layoutHeight,
- row = -1,
- column = -1
- )
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt
index 74a6896e2c9..ecf9ff7f2ec 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItem.kt
@@ -28,27 +28,27 @@ import androidx.compose.ui.util.fastForEach
* if the user emit multiple layout nodes in the item callback.
*/
internal class LazyGridMeasuredItem(
- val index: Int,
- val key: Any,
- private val isVertical: Boolean,
+ override val index: Int,
+ override val key: Any,
+ val isVertical: Boolean,
/**
* Cross axis size is the same for all [placeables]. Take it as parameter for the case when
* [placeables] is empty.
*/
val crossAxisSize: Int,
- val mainAxisSpacing: Int,
+ mainAxisSpacing: Int,
private val reverseLayout: Boolean,
private val layoutDirection: LayoutDirection,
private val beforeContentPadding: Int,
private val afterContentPadding: Int,
- val placeables: List<Placeable>,
+ private val placeables: List<Placeable>,
/**
* The offset which shouldn't affect any calculations but needs to be applied for the final
* value passed into the place() call.
*/
private val visualOffset: IntOffset,
- private val contentType: Any?
-) {
+ override val contentType: Any?
+) : LazyGridItemInfo {
/**
* Main axis size of the item - the max main axis size of the placeables.
*/
@@ -61,6 +61,10 @@ internal class LazyGridMeasuredItem(
val placeablesCount: Int get() = placeables.size
+ private var mainAxisLayoutSize: Int = Unset
+ private var minMainAxisOffset: Int = 0
+ private var maxMainAxisOffset: Int = 0
+
fun getParentData(index: Int) = placeables[index].parentData
init {
@@ -72,6 +76,19 @@ internal class LazyGridMeasuredItem(
mainAxisSizeWithSpacings = (maxMainAxis + mainAxisSpacing).coerceAtLeast(0)
}
+ override val size: IntSize = if (isVertical) {
+ IntSize(crossAxisSize, mainAxisSize)
+ } else {
+ IntSize(mainAxisSize, crossAxisSize)
+ }
+ override var offset: IntOffset = IntOffset.Zero
+ private set
+ val crossAxisOffset get() = if (isVertical) offset.x else offset.y
+ override var row: Int = LazyGridItemInfo.UnknownRow
+ private set
+ override var column: Int = LazyGridItemInfo.UnknownColumn
+ private set
+
/**
* Calculates positions for the inner placeables at [mainAxisOffset], [crossAxisOffset].
* [layoutWidth] and [layoutHeight] should be provided to not place placeables which are ended
@@ -84,10 +101,10 @@ internal class LazyGridMeasuredItem(
crossAxisOffset: Int,
layoutWidth: Int,
layoutHeight: Int,
- row: Int,
- column: Int
- ): LazyGridPositionedItem {
- val mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
+ row: Int = LazyGridItemInfo.UnknownRow,
+ column: Int = LazyGridItemInfo.UnknownColumn
+ ) {
+ mainAxisLayoutSize = if (isVertical) layoutHeight else layoutWidth
val crossAxisLayoutSize = if (isVertical) layoutWidth else layoutHeight
@Suppress("NAME_SHADOWING")
val crossAxisOffset = if (isVertical && layoutDirection == LayoutDirection.Rtl) {
@@ -95,62 +112,21 @@ internal class LazyGridMeasuredItem(
} else {
crossAxisOffset
}
- return LazyGridPositionedItem(
- offset = if (isVertical) {
- IntOffset(crossAxisOffset, mainAxisOffset)
- } else {
- IntOffset(mainAxisOffset, crossAxisOffset)
- },
- index = index,
- key = key,
- row = row,
- column = column,
- size = if (isVertical) {
- IntSize(crossAxisSize, mainAxisSize)
- } else {
- IntSize(mainAxisSize, crossAxisSize)
- },
- minMainAxisOffset = -beforeContentPadding,
- maxMainAxisOffset = mainAxisLayoutSize + afterContentPadding,
- isVertical = isVertical,
- placeables = placeables,
- visualOffset = visualOffset,
- mainAxisLayoutSize = mainAxisLayoutSize,
- reverseLayout = reverseLayout,
- contentType = contentType
- )
+ offset = if (isVertical) {
+ IntOffset(crossAxisOffset, mainAxisOffset)
+ } else {
+ IntOffset(mainAxisOffset, crossAxisOffset)
+ }
+ this.row = row
+ this.column = column
+ minMainAxisOffset = -beforeContentPadding
+ maxMainAxisOffset = mainAxisLayoutSize + afterContentPadding
}
-}
-
-internal class LazyGridPositionedItem(
- override val offset: IntOffset,
- override val index: Int,
- override val key: Any,
- override val row: Int,
- override val column: Int,
- override val size: IntSize,
- private val minMainAxisOffset: Int,
- private val maxMainAxisOffset: Int,
- val isVertical: Boolean,
- private val placeables: List<Placeable>,
- private val visualOffset: IntOffset,
- private val mainAxisLayoutSize: Int,
- private val reverseLayout: Boolean,
- override val contentType: Any?
-) : LazyGridItemInfo {
- val placeablesCount: Int get() = placeables.size
-
- fun getMainAxisSize() = if (isVertical) size.height else size.width
-
- fun getCrossAxisSize() = if (isVertical) size.width else size.height
-
- fun getCrossAxisOffset() = if (isVertical) offset.x else offset.y
-
- fun getParentData(index: Int) = placeables[index].parentData
fun place(
scope: Placeable.PlacementScope,
) = with(scope) {
+ require(mainAxisLayoutSize != Unset) { "position() should be called first" }
repeat(placeablesCount) { index ->
val placeable = placeables[index]
val minOffset = minMainAxisOffset - placeable.mainAxisSize
@@ -187,3 +163,5 @@ internal class LazyGridPositionedItem(
private inline fun IntOffset.copy(mainAxisMap: (Int) -> Int): IntOffset =
IntOffset(if (isVertical) x else mainAxisMap(x), if (isVertical) mainAxisMap(y) else y)
}
+
+private const val Unset = Int.MIN_VALUE \ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt
index c9846316c71..18d87a3e284 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredItemProvider.kt
@@ -26,11 +26,10 @@ import androidx.compose.ui.unit.Constraints
* Abstracts away the subcomposition from the measuring logic.
*/
@OptIn(ExperimentalFoundationApi::class)
-internal class LazyGridMeasuredItemProvider @ExperimentalFoundationApi constructor(
+internal abstract class LazyGridMeasuredItemProvider @ExperimentalFoundationApi constructor(
private val itemProvider: LazyGridItemProvider,
private val measureScope: LazyLayoutMeasureScope,
- private val defaultMainAxisSpacing: Int,
- private val measuredItemFactory: MeasuredItemFactory
+ private val defaultMainAxisSpacing: Int
) {
/**
* Used to subcompose individual items of lazy grids. Composed placeables will be measured
@@ -50,7 +49,7 @@ internal class LazyGridMeasuredItemProvider @ExperimentalFoundationApi construct
require(constraints.hasFixedHeight)
constraints.minHeight
}
- return measuredItemFactory.createItem(
+ return createItem(
index,
key,
contentType,
@@ -65,11 +64,8 @@ internal class LazyGridMeasuredItemProvider @ExperimentalFoundationApi construct
* the list as an optimization.
**/
val keyIndexMap: LazyLayoutKeyIndexMap = itemProvider.keyIndexMap
-}
-// This interface allows to avoid autoboxing on index param
-internal fun interface MeasuredItemFactory {
- fun createItem(
+ abstract fun createItem(
index: Int,
key: Any,
contentType: Any?,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt
index 0b3f5e04527..f5f8349b8a1 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLine.kt
@@ -66,9 +66,9 @@ internal class LazyGridMeasuredLine constructor(
offset: Int,
layoutWidth: Int,
layoutHeight: Int
- ): List<LazyGridPositionedItem> {
+ ): Array<LazyGridMeasuredItem> {
var usedSpan = 0
- return items.mapIndexed { itemIndex, item ->
+ items.forEachIndexed { itemIndex, item ->
val span = spans[itemIndex].currentLineSpan
val startSlot = usedSpan
@@ -83,5 +83,6 @@ internal class LazyGridMeasuredLine constructor(
usedSpan += span
}
}
+ return items
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt
index fe5ff5498b3..c852c3f3fca 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridMeasuredLineProvider.kt
@@ -24,14 +24,13 @@ import androidx.compose.ui.unit.Constraints
* Abstracts away subcomposition and span calculation from the measuring logic of entire lines.
*/
@OptIn(ExperimentalFoundationApi::class)
-internal class LazyGridMeasuredLineProvider(
+internal abstract class LazyGridMeasuredLineProvider(
private val isVertical: Boolean,
private val slots: LazyGridSlots,
private val gridItemsCount: Int,
private val spaceBetweenLines: Int,
private val measuredItemProvider: LazyGridMeasuredItemProvider,
- private val spanLayoutProvider: LazyGridSpanLayoutProvider,
- private val measuredLineFactory: MeasuredLineFactory
+ private val spanLayoutProvider: LazyGridSpanLayoutProvider
) {
// The constraints for cross axis size. The main axis is not restricted.
internal fun childConstraints(startSlot: Int, span: Int): Constraints {
@@ -67,7 +66,8 @@ internal class LazyGridMeasuredLineProvider(
// we add space between lines as an extra spacing for all lines apart from the last one
// so the lazy grid measuring logic will take it into account.
val mainAxisSpacing = if (lineItemsCount == 0 ||
- lineConfiguration.firstItemIndex + lineItemsCount == gridItemsCount) {
+ lineConfiguration.firstItemIndex + lineItemsCount == gridItemsCount
+ ) {
0
} else {
spaceBetweenLines
@@ -83,7 +83,7 @@ internal class LazyGridMeasuredLineProvider(
constraints
).also { startSlot += span }
}
- return measuredLineFactory.createLine(
+ return createLine(
lineIndex,
items,
lineConfiguration.spans,
@@ -96,12 +96,8 @@ internal class LazyGridMeasuredLineProvider(
* the list as an optimization.
**/
val keyIndexMap: LazyLayoutKeyIndexMap get() = measuredItemProvider.keyIndexMap
-}
-// This interface allows to avoid autoboxing on index param
-@OptIn(ExperimentalFoundationApi::class)
-internal fun interface MeasuredLineFactory {
- fun createLine(
+ abstract fun createLine(
index: Int,
items: Array<LazyGridMeasuredItem>,
spans: List<GridItemSpan>,
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
index 7b196194664..c483a243934 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridScrollPosition.kt
@@ -21,7 +21,6 @@ import androidx.compose.foundation.lazy.layout.findIndexByKey
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.Snapshot
/**
* Contains the current scroll position represented by the first visible item index and the first
@@ -56,12 +55,8 @@ internal class LazyGridScrollPosition(
val scrollOffset = measureResult.firstVisibleLineScrollOffset
check(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
- Snapshot.withoutReadObservation {
- update(
- measureResult.firstVisibleLine?.items?.firstOrNull()?.index ?: 0,
- scrollOffset
- )
- }
+ val firstIndex = measureResult.firstVisibleLine?.items?.firstOrNull()?.index ?: 0
+ update(firstIndex, scrollOffset)
}
}
@@ -89,22 +84,18 @@ internal class LazyGridScrollPosition(
* there were items added or removed before our current first visible item and keep this item
* as the first visible one even given that its index has been changed.
*/
- fun updateScrollPositionIfTheFirstItemWasMoved(itemProvider: LazyGridItemProvider) {
- Snapshot.withoutReadObservation {
- update(
- itemProvider.findIndexByKey(lastKnownFirstItemKey, index),
- scrollOffset
- )
- }
+ fun updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider: LazyGridItemProvider,
+ index: Int
+ ): Int {
+ val newIndex = itemProvider.findIndexByKey(lastKnownFirstItemKey, index)
+ this.index = newIndex
+ return newIndex
}
private fun update(index: Int, scrollOffset: Int) {
require(index >= 0f) { "Index should be non-negative ($index)" }
- if (index != this.index) {
- this.index = index
- }
- if (scrollOffset != this.scrollOffset) {
- this.scrollOffset = scrollOffset
- }
+ this.index = index
+ this.scrollOffset = scrollOffset
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
index 73e2a21b4f7..43920ef197d 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/grid/LazyGridState.kt
@@ -38,6 +38,7 @@ import androidx.compose.runtime.saveable.Saver
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.ui.layout.Remeasurement
import androidx.compose.ui.layout.RemeasurementModifier
import androidx.compose.ui.unit.Constraints
@@ -155,12 +156,12 @@ class LazyGridState constructor(
/**
* Needed for [animateScrollToItem]. Updated on every measure.
*/
- internal var density: Density by mutableStateOf(Density(1f, 1f))
+ internal var density: Density = Density(1f, 1f)
/**
* Needed for [notifyPrefetch].
*/
- internal var isVertical: Boolean by mutableStateOf(true)
+ internal var isVertical: Boolean = true
/**
* The ScrollableController instance. We keep it as we need to call stopAnimation on it once
@@ -202,7 +203,7 @@ class LazyGridState constructor(
* The [Remeasurement] object associated with our layout. It allows us to remeasure
* synchronously during scroll.
*/
- internal var remeasurement: Remeasurement? by mutableStateOf(null)
+ internal var remeasurement: Remeasurement? = null
/**
* The modifier which provides [remeasurement].
@@ -428,9 +429,10 @@ class LazyGridState constructor(
* items added or removed before our current first visible item and keep this item
* as the first visible one even given that its index has been changed.
*/
- internal fun updateScrollPositionIfTheFirstItemWasMoved(itemProvider: LazyGridItemProvider) {
- scrollPosition.updateScrollPositionIfTheFirstItemWasMoved(itemProvider)
- }
+ internal fun updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider: LazyGridItemProvider,
+ firstItemIndex: Int = Snapshot.withoutReadObservation { scrollPosition.index }
+ ): Int = scrollPosition.updateScrollPositionIfTheFirstItemWasMoved(itemProvider, firstItemIndex)
companion object {
/**
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
index f9dec0d82b5..4c368126f1c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridItemPlacementAnimator.kt
@@ -41,8 +41,8 @@ internal class LazyStaggeredGridItemPlacementAnimator {
// stored to not allocate it every pass.
private val movingAwayKeys = LinkedHashSet<Any>()
- private val movingInFromStartBound = mutableListOf<LazyStaggeredGridPositionedItem>()
- private val movingInFromEndBound = mutableListOf<LazyStaggeredGridPositionedItem>()
+ private val movingInFromStartBound = mutableListOf<LazyStaggeredGridMeasuredItem>()
+ private val movingInFromEndBound = mutableListOf<LazyStaggeredGridMeasuredItem>()
private val movingAwayToStartBound = mutableListOf<LazyStaggeredGridMeasuredItem>()
private val movingAwayToEndBound = mutableListOf<LazyStaggeredGridMeasuredItem>()
@@ -55,7 +55,7 @@ internal class LazyStaggeredGridItemPlacementAnimator {
consumedScroll: Int,
layoutWidth: Int,
layoutHeight: Int,
- positionedItems: MutableList<LazyStaggeredGridPositionedItem>,
+ positionedItems: MutableList<LazyStaggeredGridMeasuredItem>,
itemProvider: LazyStaggeredGridMeasureProvider,
isVertical: Boolean,
laneCount: Int
@@ -185,10 +185,9 @@ internal class LazyStaggeredGridItemPlacementAnimator {
val mainAxisOffset = 0 - accumulatedOffsetPerLane[item.lane]
val itemInfo = keyToItemInfoMap.getValue(item.key)
- val positionedItem =
- item.position(mainAxisOffset, itemInfo.crossAxisOffset, mainAxisLayoutSize)
- positionedItems.add(positionedItem)
- startAnimationsIfNeeded(positionedItem)
+ item.position(mainAxisOffset, itemInfo.crossAxisOffset, mainAxisLayoutSize)
+ positionedItems.add(item)
+ startAnimationsIfNeeded(item)
}
accumulatedOffsetPerLane.fill(0)
}
@@ -199,10 +198,9 @@ internal class LazyStaggeredGridItemPlacementAnimator {
accumulatedOffsetPerLane[item.lane] += item.mainAxisSize
val itemInfo = keyToItemInfoMap.getValue(item.key)
- val positionedItem =
- item.position(mainAxisOffset, itemInfo.crossAxisOffset, mainAxisLayoutSize)
- positionedItems.add(positionedItem)
- startAnimationsIfNeeded(positionedItem)
+ item.position(mainAxisOffset, itemInfo.crossAxisOffset, mainAxisLayoutSize)
+ positionedItems.add(item)
+ startAnimationsIfNeeded(item)
}
}
@@ -224,7 +222,7 @@ internal class LazyStaggeredGridItemPlacementAnimator {
}
private fun initializeNode(
- item: LazyStaggeredGridPositionedItem,
+ item: LazyStaggeredGridMeasuredItem,
mainAxisOffset: Int
) {
val firstPlaceableOffset = item.offset
@@ -243,7 +241,7 @@ internal class LazyStaggeredGridItemPlacementAnimator {
}
}
- private fun startAnimationsIfNeeded(item: LazyStaggeredGridPositionedItem) {
+ private fun startAnimationsIfNeeded(item: LazyStaggeredGridMeasuredItem) {
item.forEachNode { node ->
val newTarget = item.offset
val currentTarget = node.rawOffset
@@ -258,13 +256,13 @@ internal class LazyStaggeredGridItemPlacementAnimator {
private val Any?.node get() = this as? LazyLayoutAnimateItemModifierNode
- private val LazyStaggeredGridPositionedItem.hasAnimations: Boolean
+ private val LazyStaggeredGridMeasuredItem.hasAnimations: Boolean
get() {
forEachNode { return true }
return false
}
- private inline fun LazyStaggeredGridPositionedItem.forEachNode(
+ private inline fun LazyStaggeredGridMeasuredItem.forEachNode(
block: (LazyLayoutAnimateItemModifierNode) -> Unit
) {
repeat(placeablesCount) { index ->
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
index 460d75afff0..79ad9cdcf7b 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasure.kt
@@ -109,7 +109,12 @@ internal fun LazyLayoutMeasureScope.measureStaggeredGrid(
val initialItemOffsets: IntArray
Snapshot.withoutReadObservation {
- val firstVisibleIndices = state.scrollPosition.indices
+ // ensure scroll position is up to date
+ val firstVisibleIndices =
+ state.updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider,
+ state.scrollPosition.indices
+ )
val firstVisibleOffsets = state.scrollPosition.offsets
initialItemIndices =
@@ -181,13 +186,20 @@ internal class LazyStaggeredGridMeasureContext(
val reverseLayout: Boolean,
val mainAxisSpacing: Int,
) {
- val measuredItemProvider = LazyStaggeredGridMeasureProvider(
+ val measuredItemProvider = object : LazyStaggeredGridMeasureProvider(
isVertical = isVertical,
itemProvider = itemProvider,
measureScope = measureScope,
resolvedSlots = resolvedSlots,
- ) { index, lane, span, key, contentType, placeables ->
- LazyStaggeredGridMeasuredItem(
+ ) {
+ override fun createItem(
+ index: Int,
+ lane: Int,
+ span: Int,
+ key: Any,
+ contentType: Any?,
+ placeables: List<Placeable>
+ ) = LazyStaggeredGridMeasuredItem(
index = index,
key = key,
placeables = placeables,
@@ -751,13 +763,12 @@ private fun LazyStaggeredGridMeasureContext.measure(
extraItemOffset = itemScrollOffsets[0]
val extraItemsAfter = calculateExtraItems(
position = {
- val positionedItem = it.position(
+ it.position(
mainAxis = extraItemOffset,
crossAxis = 0,
mainAxisLayoutSize = mainAxisLayoutSize
)
extraItemOffset += it.sizeWithSpacings
- positionedItem
},
filter = { itemIndex ->
if (itemIndex >= itemCount) {
@@ -775,7 +786,7 @@ private fun LazyStaggeredGridMeasureContext.measure(
}
)
- val positionedItems = mutableListOf<LazyStaggeredGridPositionedItem>()
+ val positionedItems = mutableListOf<LazyStaggeredGridMeasuredItem>()
positionedItems.addAll(extraItemsBefore)
positionedItems.addAll(visibleItems)
positionedItems.addAll(extraItemsAfter)
@@ -830,8 +841,8 @@ private fun LazyStaggeredGridMeasureContext.calculateVisibleItems(
measuredItems: Array<ArrayDeque<LazyStaggeredGridMeasuredItem>>,
itemScrollOffsets: IntArray,
mainAxisLayoutSize: Int,
-): List<LazyStaggeredGridPositionedItem> {
- val positionedItems = ArrayList<LazyStaggeredGridPositionedItem>(
+): List<LazyStaggeredGridMeasuredItem> {
+ val positionedItems = ArrayList<LazyStaggeredGridMeasuredItem>(
measuredItems.sumOf { it.size }
)
while (measuredItems.any { it.isNotEmpty() }) {
@@ -853,14 +864,12 @@ private fun LazyStaggeredGridMeasureContext.calculateVisibleItems(
// nothing to place, ignore spacings
continue
}
-
- positionedItems +=
- item.position(
- mainAxis = mainAxisOffset,
- crossAxis = crossAxisOffset,
- mainAxisLayoutSize = mainAxisLayoutSize,
- lane = laneIndex
- )
+ item.position(
+ mainAxis = mainAxisOffset,
+ crossAxis = crossAxisOffset,
+ mainAxisLayoutSize = mainAxisLayoutSize,
+ )
+ positionedItems += item
spanRange.forEach { lane ->
itemScrollOffsets[lane] = mainAxisOffset + item.sizeWithSpacings
}
@@ -870,10 +879,10 @@ private fun LazyStaggeredGridMeasureContext.calculateVisibleItems(
@ExperimentalFoundationApi
private inline fun LazyStaggeredGridMeasureContext.calculateExtraItems(
- position: (LazyStaggeredGridMeasuredItem) -> LazyStaggeredGridPositionedItem,
+ position: (LazyStaggeredGridMeasuredItem) -> Unit,
filter: (itemIndex: Int) -> Boolean
-): List<LazyStaggeredGridPositionedItem> {
- var result: MutableList<LazyStaggeredGridPositionedItem>? = null
+): List<LazyStaggeredGridMeasuredItem> {
+ var result: MutableList<LazyStaggeredGridMeasuredItem>? = null
pinnedItems.fastForEach { index ->
if (filter(index)) {
@@ -882,7 +891,8 @@ private inline fun LazyStaggeredGridMeasureContext.calculateExtraItems(
result = mutableListOf()
}
val measuredItem = measuredItemProvider.getAndMeasure(index, spanRange)
- result?.add(position(measuredItem))
+ position(measuredItem)
+ result?.add(measuredItem)
}
}
@@ -987,12 +997,11 @@ private fun LazyStaggeredGridMeasureContext.findPreviousItemIndex(item: Int, lan
laneInfo.findPreviousItemIndex(item, lane)
@OptIn(ExperimentalFoundationApi::class)
-internal class LazyStaggeredGridMeasureProvider(
+internal abstract class LazyStaggeredGridMeasureProvider(
private val isVertical: Boolean,
private val itemProvider: LazyStaggeredGridItemProvider,
private val measureScope: LazyLayoutMeasureScope,
- private val resolvedSlots: LazyStaggeredGridSlots,
- private val measuredItemFactory: MeasuredItemFactory,
+ private val resolvedSlots: LazyStaggeredGridSlots
) {
private fun childConstraints(requestedSlot: Int, requestedSpan: Int): Constraints {
val slotCount = resolvedSlots.sizes.size
@@ -1020,7 +1029,7 @@ internal class LazyStaggeredGridMeasureProvider(
val key = keyIndexMap.getKey(index) ?: itemProvider.getKey(index)
val contentType = itemProvider.getContentType(index)
val placeables = measureScope.measure(index, childConstraints(span.start, span.size))
- return measuredItemFactory.createItem(
+ return createItem(
index,
span.start,
span.size,
@@ -1031,11 +1040,8 @@ internal class LazyStaggeredGridMeasureProvider(
}
val keyIndexMap: LazyLayoutKeyIndexMap = itemProvider.keyIndexMap
-}
-// This interface allows to avoid autoboxing on index param
-internal fun interface MeasuredItemFactory {
- fun createItem(
+ abstract fun createItem(
index: Int,
lane: Int,
span: Int,
@@ -1046,17 +1052,17 @@ internal fun interface MeasuredItemFactory {
}
internal class LazyStaggeredGridMeasuredItem(
- val index: Int,
- val key: Any,
+ override val index: Int,
+ override val key: Any,
private val placeables: List<Placeable>,
- private val isVertical: Boolean,
+ val isVertical: Boolean,
spacing: Int,
- val lane: Int,
+ override val lane: Int,
val span: Int,
private val beforeContentPadding: Int,
private val afterContentPadding: Int,
- private val contentType: Any?
-) {
+ override val contentType: Any?
+) : LazyStaggeredGridItemInfo {
var isVisible = true
val placeablesCount: Int get() = placeables.size
@@ -1073,63 +1079,40 @@ internal class LazyStaggeredGridMeasuredItem(
if (isVertical) it.width else it.height
} ?: 0
+ private var mainAxisLayoutSize: Int = Unset
+ private var minMainAxisOffset: Int = 0
+ private var maxMainAxisOffset: Int = 0
+
+ override val size: IntSize = if (isVertical) {
+ IntSize(crossAxisSize, mainAxisSize)
+ } else {
+ IntSize(mainAxisSize, crossAxisSize)
+ }
+ override var offset: IntOffset = IntOffset.Zero
+ private set
+
fun position(
mainAxis: Int,
crossAxis: Int,
mainAxisLayoutSize: Int,
- lane: Int = 0
- ): LazyStaggeredGridPositionedItem =
- LazyStaggeredGridPositionedItem(
- offset = if (isVertical) {
- IntOffset(crossAxis, mainAxis)
- } else {
- IntOffset(mainAxis, crossAxis)
- },
- lane = lane,
- index = index,
- key = key,
- size = if (isVertical) {
- IntSize(crossAxisSize, mainAxisSize)
- } else {
- IntSize(mainAxisSize, crossAxisSize)
- },
- placeables = placeables,
- isVertical = isVertical,
- mainAxisLayoutSize = mainAxisLayoutSize,
- minMainAxisOffset = -beforeContentPadding,
- maxMainAxisOffset = mainAxisLayoutSize + afterContentPadding,
- span = span,
- contentType = contentType
- )
-}
-
-internal class LazyStaggeredGridPositionedItem(
- override val offset: IntOffset,
- override val index: Int,
- override val lane: Int,
- override val key: Any,
- override val size: IntSize,
- private val placeables: List<Placeable>,
- val isVertical: Boolean,
- private val mainAxisLayoutSize: Int,
- private val minMainAxisOffset: Int,
- private val maxMainAxisOffset: Int,
- val span: Int,
- override val contentType: Any?
-) : LazyStaggeredGridItemInfo {
-
- val placeablesCount: Int get() = placeables.size
-
- val mainAxisSize get() = if (isVertical) size.height else size.width
+ ) {
+ this.mainAxisLayoutSize = mainAxisLayoutSize
+ minMainAxisOffset = -beforeContentPadding
+ maxMainAxisOffset = mainAxisLayoutSize + afterContentPadding
+ offset = if (isVertical) {
+ IntOffset(crossAxis, mainAxis)
+ } else {
+ IntOffset(mainAxis, crossAxis)
+ }
+ }
val crossAxisOffset get() = if (isVertical) offset.x else offset.y
- fun getParentData(index: Int) = placeables[index].parentData
-
fun place(
scope: Placeable.PlacementScope,
context: LazyStaggeredGridMeasureContext
) = with(context) {
+ require(mainAxisLayoutSize != Unset) { "position() should be called first" }
with(scope) {
placeables.fastForEachIndexed { index, placeable ->
val minOffset = minMainAxisOffset - placeable.mainAxisSize
@@ -1170,3 +1153,5 @@ internal class LazyStaggeredGridPositionedItem(
super.toString()
}
}
+
+private const val Unset = Int.MIN_VALUE
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
index c72c1145bcb..b6d7fae7361 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridMeasurePolicy.kt
@@ -68,9 +68,6 @@ internal fun rememberStaggeredGridMeasurePolicy(
state.isVertical = isVertical
state.spanProvider = itemProvider.spanProvider
- // ensure scroll position is up to date
- state.updateScrollPositionIfTheFirstItemWasMoved(itemProvider)
-
// setup measure
val beforeContentPadding = contentPadding.beforePadding(
orientation, reverseLayout, layoutDirection
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridScrollPosition.kt
index 5d5fe3306f4..6f418011f9c 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridScrollPosition.kt
@@ -19,6 +19,7 @@ package androidx.compose.foundation.lazy.staggeredgrid
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.lazy.layout.LazyLayoutItemProvider
import androidx.compose.foundation.lazy.layout.findIndexByKey
+import androidx.compose.runtime.SnapshotMutationPolicy
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
@@ -30,9 +31,11 @@ internal class LazyStaggeredGridScrollPosition(
initialIndices: IntArray,
initialOffsets: IntArray,
private val fillIndices: (targetIndex: Int, laneCount: Int) -> IntArray
-) {
- var indices by mutableStateOf(initialIndices)
- var offsets by mutableStateOf(initialOffsets)
+) : SnapshotMutationPolicy<IntArray> {
+ var indices by mutableStateOf(initialIndices, this)
+ private set
+ var offsets by mutableStateOf(initialOffsets, this)
+ private set
private var hadFirstNotEmptyLayout = false
@@ -91,27 +94,28 @@ internal class LazyStaggeredGridScrollPosition(
* as the first visible one even given that its index has been changed.
*/
@ExperimentalFoundationApi
- fun updateScrollPositionIfTheFirstItemWasMoved(itemProvider: LazyLayoutItemProvider) {
- Snapshot.withoutReadObservation {
- val lastIndex = itemProvider.findIndexByKey(
- key = lastKnownFirstItemKey,
- lastKnownIndex = indices.getOrNull(0) ?: 0
- )
- if (lastIndex !in indices) {
- update(
- fillIndices(lastIndex, indices.size),
- offsets
- )
- }
+ fun updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider: LazyLayoutItemProvider,
+ indices: IntArray
+ ): IntArray {
+ val lastIndex = itemProvider.findIndexByKey(
+ key = lastKnownFirstItemKey,
+ lastKnownIndex = indices.getOrNull(0) ?: 0
+ )
+ return if (lastIndex !in indices) {
+ val newIndices = fillIndices(lastIndex, indices.size)
+ this.indices = newIndices
+ newIndices
+ } else {
+ indices
}
}
private fun update(indices: IntArray, offsets: IntArray) {
- if (!indices.contentEquals(this.indices)) {
- this.indices = indices
- }
- if (!offsets.contentEquals(this.offsets)) {
- this.offsets = offsets
- }
+ this.indices = indices
+ this.offsets = offsets
}
+
+ // mutation policy for int arrays
+ override fun equivalent(a: IntArray, b: IntArray) = a.contentEquals(b)
} \ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
index 84f9df6a64c..1c6233d8e89 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/lazy/staggeredgrid/LazyStaggeredGridState.kt
@@ -39,6 +39,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.listSaver
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.runtime.snapshots.Snapshot
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.layout.Remeasurement
import androidx.compose.ui.layout.RemeasurementModifier
@@ -331,9 +332,11 @@ class LazyStaggeredGridState private constructor(
/**
* Maintain scroll position for item based on custom key if its index has changed.
*/
- internal fun updateScrollPositionIfTheFirstItemWasMoved(itemProvider: LazyLayoutItemProvider) {
- scrollPosition.updateScrollPositionIfTheFirstItemWasMoved(itemProvider)
- }
+ internal fun updateScrollPositionIfTheFirstItemWasMoved(
+ itemProvider: LazyLayoutItemProvider,
+ firstItemIndex: IntArray = Snapshot.withoutReadObservation { scrollPosition.indices }
+ ): IntArray =
+ scrollPosition.updateScrollPositionIfTheFirstItemWasMoved(itemProvider, firstItemIndex)
override fun dispatchRawDelta(delta: Float): Float =
scrollableState.dispatchRawDelta(delta)
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/MeasuredPage.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/MeasuredPage.kt
index 8543acdf57f..8ad0a3ec072 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/MeasuredPage.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/MeasuredPage.kt
@@ -16,86 +16,103 @@
package androidx.compose.foundation.pager
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.ui.Alignment
import androidx.compose.ui.layout.Placeable
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.util.fastForEach
+import androidx.compose.ui.util.fastForEachIndexed
+@OptIn(ExperimentalFoundationApi::class)
internal class MeasuredPage(
- val index: Int,
+ override val index: Int,
val size: Int,
- val placeables: List<Placeable>,
- val visualOffset: IntOffset,
+ private val placeables: List<Placeable>,
+ private val visualOffset: IntOffset,
val key: Any,
- val orientation: Orientation,
- val horizontalAlignment: Alignment.Horizontal?,
- val verticalAlignment: Alignment.Vertical?,
- val layoutDirection: LayoutDirection,
- val reverseLayout: Boolean,
- val beforeContentPadding: Int,
- val afterContentPadding: Int,
-) {
+ orientation: Orientation,
+ private val horizontalAlignment: Alignment.Horizontal?,
+ private val verticalAlignment: Alignment.Vertical?,
+ private val layoutDirection: LayoutDirection,
+ private val reverseLayout: Boolean
+) : PageInfo {
+
+ private val isVertical = orientation == Orientation.Vertical
val crossAxisSize: Int
+ // optimized for storing x and y offsets for each placeable one by one.
+ // array's size == placeables.size * 2, first we store x, then y.
+ private val placeableOffsets: IntArray
+
init {
var maxCrossAxis = 0
placeables.fastForEach {
maxCrossAxis = maxOf(
maxCrossAxis,
- if (orientation != Orientation.Vertical) it.height else it.width
+ if (!isVertical) it.height else it.width
)
}
crossAxisSize = maxCrossAxis
+ placeableOffsets = IntArray(placeables.size * 2)
}
+ override var offset: Int = 0
+ private set
+
+ private var mainAxisLayoutSize: Int = Unset
+
fun position(
offset: Int,
layoutWidth: Int,
layoutHeight: Int
- ): PositionedPage {
- val wrappers = mutableListOf<PagerPlaceableWrapper>()
- val mainAxisLayoutSize =
- if (orientation == Orientation.Vertical) layoutHeight else layoutWidth
- var mainAxisOffset = if (reverseLayout) {
- mainAxisLayoutSize - offset - size
- } else {
- offset
+ ) {
+ this.offset = offset
+ mainAxisLayoutSize =
+ if (isVertical) layoutHeight else layoutWidth
+ var mainAxisOffset = offset
+ placeables.fastForEachIndexed { index, placeable ->
+ val indexInArray = index * 2
+ if (isVertical) {
+ placeableOffsets[indexInArray] = requireNotNull(horizontalAlignment)
+ .align(placeable.width, layoutWidth, layoutDirection)
+ placeableOffsets[indexInArray + 1] = mainAxisOffset
+ mainAxisOffset += placeable.height
+ } else {
+ placeableOffsets[indexInArray] = mainAxisOffset
+ placeableOffsets[indexInArray + 1] = requireNotNull(verticalAlignment)
+ .align(placeable.height, layoutHeight)
+ mainAxisOffset += placeable.width
+ }
}
- var index = if (reverseLayout) placeables.lastIndex else 0
- while (if (reverseLayout) index >= 0 else index < placeables.size) {
- val it = placeables[index]
- val addIndex = if (reverseLayout) 0 else wrappers.size
- val placeableOffset = if (orientation == Orientation.Vertical) {
- val x = requireNotNull(horizontalAlignment)
- .align(it.width, layoutWidth, layoutDirection)
- IntOffset(x, mainAxisOffset)
+ }
+
+ fun place(scope: Placeable.PlacementScope) = with(scope) {
+ require(mainAxisLayoutSize != Unset) { "position() should be called first" }
+ repeat(placeables.size) { index ->
+ val placeable = placeables[index]
+ var offset = getOffset(index)
+ if (reverseLayout) {
+ offset = offset.copy { mainAxisOffset ->
+ mainAxisLayoutSize - mainAxisOffset - placeable.mainAxisSize
+ }
+ }
+ offset += visualOffset
+ if (isVertical) {
+ placeable.placeWithLayer(offset)
} else {
- val y = requireNotNull(verticalAlignment).align(it.height, layoutHeight)
- IntOffset(mainAxisOffset, y)
+ placeable.placeRelativeWithLayer(offset)
}
- mainAxisOffset += if (orientation == Orientation.Vertical) it.height else it.width
- wrappers.add(
- addIndex,
- PagerPlaceableWrapper(placeableOffset, it, placeables[index].parentData)
- )
- if (reverseLayout) index-- else index++
}
- return PositionedPage(
- offset = offset,
- index = this.index,
- key = key,
- orientation = orientation,
- wrappers = wrappers,
- visualOffset = visualOffset,
- )
}
+
+ private fun getOffset(index: Int) =
+ IntOffset(placeableOffsets[index * 2], placeableOffsets[index * 2 + 1])
+ private val Placeable.mainAxisSize get() = if (isVertical) height else width
+ private inline fun IntOffset.copy(mainAxisMap: (Int) -> Int): IntOffset =
+ IntOffset(if (isVertical) x else mainAxisMap(x), if (isVertical) mainAxisMap(y) else y)
}
-internal class PagerPlaceableWrapper(
- val offset: IntOffset,
- val placeable: Placeable,
- val parentData: Any?
-) \ No newline at end of file
+private const val Unset = Int.MIN_VALUE
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
index 4c6eb096ab9..90e0f3db618 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerMeasure.kt
@@ -119,7 +119,7 @@ internal fun LazyLayoutMeasureScope.measurePager(
}
// this will contain all the measured pages representing the visible pages
- val visiblePages = mutableListOf<MeasuredPage>()
+ val visiblePages = ArrayDeque<MeasuredPage>()
// define min and max offsets
val minOffset = -beforeContentPadding + if (spaceBetweenPages < 0) spaceBetweenPages else 0
@@ -146,8 +146,6 @@ internal fun LazyLayoutMeasureScope.measurePager(
orientation = orientation,
horizontalAlignment = horizontalAlignment,
verticalAlignment = verticalAlignment,
- afterContentPadding = afterContentPadding,
- beforeContentPadding = beforeContentPadding,
layoutDirection = layoutDirection,
reverseLayout = reverseLayout,
pageAvailableSize = pageAvailableSize
@@ -194,8 +192,6 @@ internal fun LazyLayoutMeasureScope.measurePager(
orientation = orientation,
horizontalAlignment = horizontalAlignment,
verticalAlignment = verticalAlignment,
- afterContentPadding = afterContentPadding,
- beforeContentPadding = beforeContentPadding,
layoutDirection = layoutDirection,
reverseLayout = reverseLayout,
pageAvailableSize = pageAvailableSize
@@ -232,8 +228,6 @@ internal fun LazyLayoutMeasureScope.measurePager(
orientation = orientation,
horizontalAlignment = horizontalAlignment,
verticalAlignment = verticalAlignment,
- afterContentPadding = afterContentPadding,
- beforeContentPadding = beforeContentPadding,
layoutDirection = layoutDirection,
reverseLayout = reverseLayout,
pageAvailableSize = pageAvailableSize
@@ -298,8 +292,6 @@ internal fun LazyLayoutMeasureScope.measurePager(
orientation = orientation,
horizontalAlignment = horizontalAlignment,
verticalAlignment = verticalAlignment,
- afterContentPadding = afterContentPadding,
- beforeContentPadding = beforeContentPadding,
layoutDirection = layoutDirection,
reverseLayout = reverseLayout,
pageAvailableSize = pageAvailableSize
@@ -326,8 +318,6 @@ internal fun LazyLayoutMeasureScope.measurePager(
orientation = orientation,
horizontalAlignment = horizontalAlignment,
verticalAlignment = verticalAlignment,
- afterContentPadding = afterContentPadding,
- beforeContentPadding = beforeContentPadding,
layoutDirection = layoutDirection,
reverseLayout = reverseLayout,
pageAvailableSize = pageAvailableSize
@@ -478,8 +468,6 @@ private fun LazyLayoutMeasureScope.getAndMeasure(
orientation: Orientation,
horizontalAlignment: Alignment.Horizontal?,
verticalAlignment: Alignment.Vertical?,
- afterContentPadding: Int,
- beforeContentPadding: Int,
layoutDirection: LayoutDirection,
reverseLayout: Boolean,
pageAvailableSize: Int
@@ -493,8 +481,6 @@ private fun LazyLayoutMeasureScope.getAndMeasure(
visualOffset = visualPageOffset,
horizontalAlignment = horizontalAlignment,
verticalAlignment = verticalAlignment,
- afterContentPadding = afterContentPadding,
- beforeContentPadding = beforeContentPadding,
layoutDirection = layoutDirection,
reverseLayout = reverseLayout,
size = pageAvailableSize,
@@ -518,7 +504,7 @@ private fun LazyLayoutMeasureScope.calculatePagesOffsets(
density: Density,
spaceBetweenPages: Int,
pageAvailableSize: Int
-): MutableList<PositionedPage> {
+): MutableList<MeasuredPage> {
val pageSizeWithSpacing = (pageAvailableSize + spaceBetweenPages)
val mainAxisLayoutSize = if (orientation == Orientation.Vertical) layoutHeight else layoutWidth
val hasSpareSpace = finalMainAxisOffset < minOf(mainAxisLayoutSize, maxOffset)
@@ -526,7 +512,7 @@ private fun LazyLayoutMeasureScope.calculatePagesOffsets(
check(pagesScrollOffset == 0)
}
val positionedPages =
- ArrayList<PositionedPage>(pages.size + extraPagesBefore.size + extraPagesAfter.size)
+ ArrayList<MeasuredPage>(pages.size + extraPagesBefore.size + extraPagesAfter.size)
if (hasSpareSpace) {
require(extraPagesBefore.isEmpty() && extraPagesAfter.isEmpty())
@@ -560,23 +546,27 @@ private fun LazyLayoutMeasureScope.calculatePagesOffsets(
} else {
absoluteOffset
}
- positionedPages.add(page.position(relativeOffset, layoutWidth, layoutHeight))
+ page.position(relativeOffset, layoutWidth, layoutHeight)
+ positionedPages.add(page)
}
} else {
var currentMainAxis = pagesScrollOffset
extraPagesBefore.fastForEach {
currentMainAxis -= pageSizeWithSpacing
- positionedPages.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+ it.position(currentMainAxis, layoutWidth, layoutHeight)
+ positionedPages.add(it)
}
currentMainAxis = pagesScrollOffset
pages.fastForEach {
- positionedPages.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+ it.position(currentMainAxis, layoutWidth, layoutHeight)
+ positionedPages.add(it)
currentMainAxis += pageSizeWithSpacing
}
extraPagesAfter.fastForEach {
- positionedPages.add(it.position(currentMainAxis, layoutWidth, layoutHeight))
+ it.position(currentMainAxis, layoutWidth, layoutHeight)
+ positionedPages.add(it)
currentMainAxis += pageSizeWithSpacing
}
}
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
index cecb3717f04..500b396ef88 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerScrollPosition.kt
@@ -20,7 +20,6 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.setValue
-import androidx.compose.runtime.snapshots.Snapshot
/**
* Contains the current scroll position represented by the first visible page and the first
@@ -55,15 +54,13 @@ internal class PagerScrollPosition(
val scrollOffset = measureResult.firstVisiblePageOffset
check(scrollOffset >= 0f) { "scrollOffset should be non-negative ($scrollOffset)" }
- Snapshot.withoutReadObservation {
- update(
- measureResult.firstVisiblePage?.index ?: 0,
- scrollOffset
- )
- measureResult.closestPageToSnapPosition?.index?.let {
- if (it != this.currentPage) {
- this.currentPage = it
- }
+ update(
+ measureResult.firstVisiblePage?.index ?: 0,
+ scrollOffset
+ )
+ measureResult.closestPageToSnapPosition?.index?.let {
+ if (it != this.currentPage) {
+ this.currentPage = it
}
}
}
@@ -89,11 +86,7 @@ internal class PagerScrollPosition(
private fun update(index: Int, scrollOffset: Int) {
require(index >= 0f) { "Index should be non-negative ($index)" }
- if (index != this.firstVisiblePage) {
- this.firstVisiblePage = index
- }
- if (scrollOffset != this.scrollOffset) {
- this.scrollOffset = scrollOffset
- }
+ this.firstVisiblePage = index
+ this.scrollOffset = scrollOffset
}
} \ No newline at end of file
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
index ea799c26ccf..fd85de16aa4 100644
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
+++ b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PagerState.kt
@@ -240,7 +240,7 @@ abstract class PagerState(
internal val pageSize: Int
get() = pagerLayoutInfoState.value.pageSize
- internal var density: Density by mutableStateOf(UnitDensity)
+ internal var density: Density = UnitDensity
private val visiblePages: List<PageInfo>
get() = pagerLayoutInfoState.value.visiblePagesInfo
@@ -397,7 +397,7 @@ abstract class PagerState(
/**
* Constraints passed to the prefetcher for premeasuring the prefetched items.
*/
- internal var premeasureConstraints by mutableStateOf(Constraints())
+ internal var premeasureConstraints = Constraints()
/**
* Stores currently pinned pages which are always composed, used by for beyond bound pages.
diff --git a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PositionedPage.kt b/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PositionedPage.kt
deleted file mode 100644
index 57ce77f2c02..00000000000
--- a/compose/foundation/foundation/src/commonMain/kotlin/androidx/compose/foundation/pager/PositionedPage.kt
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * Copyright 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package androidx.compose.foundation.pager
-
-import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.gestures.Orientation
-import androidx.compose.ui.layout.Placeable
-import androidx.compose.ui.unit.IntOffset
-
-@OptIn(ExperimentalFoundationApi::class)
-internal class PositionedPage(
- override val index: Int,
- override val offset: Int,
- val key: Any,
- val orientation: Orientation,
- val wrappers: MutableList<PagerPlaceableWrapper>,
- val visualOffset: IntOffset
-) : PageInfo {
- fun place(scope: Placeable.PlacementScope) = with(scope) {
- repeat(wrappers.size) { index ->
- val placeable = wrappers[index].placeable
- val offset = wrappers[index].offset
- if (orientation == Orientation.Vertical) {
- placeable.placeWithLayer(offset + visualOffset)
- } else {
- placeable.placeRelativeWithLayer(offset + visualOffset)
- }
- }
- }
-} \ No newline at end of file