Source set: Android
public val layoutInfo: ScalingLazyListLayoutInfo by
derivedStateOf(referentialEqualityPolicy()) {
val config = config.value
if (config == null) {
EmptyScalingLazyListLayoutInfo
} else {
// read list state once here to for performance reasons and to ensure consistency
val lazyListLayoutInfo = lazyListState.layoutInfo
val initialized = initialized.value
val visibleItemsInfo = mutableListOf<ScalingLazyListItemInfo>()
var newCenterItemIndex = 0
var newCenterItemScrollOffset = 0
val visible = initialized || config.localInspectionMode
// The verticalAdjustment is used to allow for the extraPadding that the
// ScalingLazyColumn employs to ensure that there are sufficient list items composed
// by the underlying LazyList even when there is extreme scaling being applied that
// could result in additional list items be eligible to be drawn.
// It is important to adjust for this extra space when working out the viewport
// center-line based coordinate system of the ScalingLazyList.
val verticalAdjustment =
lazyListLayoutInfo.viewportStartOffset + config.extraPaddingPx
// Find the item in the middle of the viewport
val centralItemArrayIndex =
findItemNearestCenter(lazyListLayoutInfo, config, verticalAdjustment)
if (centralItemArrayIndex != null) {
val originalVisibleItemsInfo = lazyListLayoutInfo.visibleItemsInfo
val centralItem = originalVisibleItemsInfo[centralItemArrayIndex]
// Place the center item
val centerItemInfo: ScalingLazyListItemInfo =
calculateItemInfo(
centralItem.offset,
centralItem,
verticalAdjustment,
config.viewportHeightPx,
config.viewportCenterLinePx(),
config.scalingParams,
config.beforeContentPaddingPx,
config.anchorType,
config.autoCentering,
visible,
)
visibleItemsInfo.add(centerItemInfo)
newCenterItemIndex = centerItemInfo.index
newCenterItemScrollOffset = -centerItemInfo.offset
// Find the adjusted position of the central item in the coordinate system of
// the
// underlying LazyColumn by adjusting for any scaling
val centralItemAdjustedUnderlyingOffset =
centralItem.offset +
((centerItemInfo.startOffset(config.anchorType) -
centerItemInfo.unadjustedStartOffset(config.anchorType)))
.roundToInt()
// Go Up
// nextItemBottomNoPadding uses the coordinate system of the underlying
// LazyList. It
// keeps track of the top of the next potential list item that is a candidate to
// be
// drawn in the viewport as we walk up the list items from the center. Going up
// involved making offset smaller/negative as the coordinate system of the
// LazyList
// starts at the top of the viewport. Note that the start of the lazy list
// coordinates starts at '- start content padding in pixels' and goes beyond the
// last visible list items to include the end content padding in pixels.
// centralItem.offset is a startOffset in the coordinate system of the
// underlying lazy list.
var nextItemBottomNoPadding =
centralItemAdjustedUnderlyingOffset - config.gapBetweenItemsPx
(centralItemArrayIndex - 1 downTo 0).forEach { ix ->
if (nextItemBottomNoPadding >= verticalAdjustment) {
val currentItem = lazyListLayoutInfo.visibleItemsInfo[ix]
if (
!discardAutoCenteringListItem(
config,
lazyListLayoutInfo,
currentItem,
)
) {
val itemInfo =
calculateItemInfo(
nextItemBottomNoPadding - currentItem.size,
currentItem,
verticalAdjustment,
config.viewportHeightPx,
config.viewportCenterLinePx(),
config.scalingParams,
config.beforeContentPaddingPx,
config.anchorType,
config.autoCentering,
visible,
)
visibleItemsInfo.add(0, itemInfo)
nextItemBottomNoPadding =
nextItemBottomNoPadding -
itemInfo.size -
config.gapBetweenItemsPx
}
} else {
return@forEach
}
}
// Go Down
// nextItemTopNoPadding uses the coordinate system of the underlying LazyList.
// It
// keeps track of the top of the next potential list item that is a candidate to
// be
// drawn in the viewport as we walk down the list items from the center.
var nextItemTopNoPadding =
centralItemAdjustedUnderlyingOffset +
centerItemInfo.size +
config.gapBetweenItemsPx
(((centralItemArrayIndex + 1) until originalVisibleItemsInfo.size)).forEach { ix
->
if (
(nextItemTopNoPadding - config.viewportHeightPx) <= verticalAdjustment
) {
val currentItem = lazyListLayoutInfo.visibleItemsInfo[ix]
if (
!discardAutoCenteringListItem(
config,
lazyListLayoutInfo,
currentItem,
)
) {
val itemInfo =
calculateItemInfo(
nextItemTopNoPadding,
currentItem,
verticalAdjustment,
config.viewportHeightPx,
config.viewportCenterLinePx(),
config.scalingParams,
config.beforeContentPaddingPx,
config.anchorType,
config.autoCentering,
visible,
)
visibleItemsInfo.add(itemInfo)
nextItemTopNoPadding += itemInfo.size + config.gapBetweenItemsPx
}
} else {
return@forEach
}
}
}
val totalItemsCount =
if (config.autoCentering != null) {
(lazyListLayoutInfo.totalItemsCount - 2).coerceAtLeast(0)
} else {
lazyListLayoutInfo.totalItemsCount
}
// Decide if we are ready for the 2nd stage of initialization
// 1. We are not yet initialized and
val readyForInitialScroll =
if (!initialized) {
// 1. autoCentering is off or
// 2. The list has no items or
// 3. the before content autoCentering Spacer has been sized.
// NOTE: It is possible, if the first real item in the list is large, that
// the size
// of the Spacer is 0.
config.autoCentering == null ||
(lazyListLayoutInfo.visibleItemsInfo.size >= 2 &&
(
// or Empty list (other than the 2 spacers)
lazyListLayoutInfo.visibleItemsInfo.size == 2 ||
// or first item is correctly size
topSpacerIsCorrectlySized(
config,
lazyListLayoutInfo.visibleItemsInfo,
lazyListLayoutInfo.totalItemsCount,
)))
} else {
// We are already initialized and have an incomplete scroll to finish
incompleteScrollItem.value != null
}
DefaultScalingLazyListLayoutInfo(
internalVisibleItemsInfo = visibleItemsInfo,
totalItemsCount = totalItemsCount,
viewportStartOffset =
lazyListLayoutInfo.viewportStartOffset + config.extraPaddingPx,
viewportEndOffset =
lazyListLayoutInfo.viewportEndOffset - config.extraPaddingPx,
centerItemIndex = if (initialized) newCenterItemIndex else 0,
centerItemScrollOffset = if (initialized) newCenterItemScrollOffset else 0,
reverseLayout = config.reverseLayout,
orientation = lazyListLayoutInfo.orientation,
viewportSize =
IntSize(
width = lazyListLayoutInfo.viewportSize.width,
height =
lazyListLayoutInfo.viewportSize.height - config.extraPaddingPx * 2,
),
beforeContentPadding = config.beforeContentPaddingPx,
afterContentPadding = config.afterContentPaddingPx,
beforeAutoCenteringPadding =
calculateTopAutoCenteringPaddingPx(
config,
visibleItemsInfo,
totalItemsCount,
),
afterAutoCenteringPadding =
calculateBottomAutoCenteringPaddingPx(
config,
visibleItemsInfo,
totalItemsCount,
),
readyForInitialScroll = readyForInitialScroll,
initialized = initialized,
anchorType = config.anchorType,
)
}
}
The object of ScalingLazyListLayoutInfo calculated during the last layout pass. For example, you can use it to calculate what items are currently visible.