Как избежать ненужных рекомпозиций в LazyRow
У меня есть LazyRow с несколькими ячейками, и он отслеживает элемент, который находится в центре экрана. Проблема в том, что для отслеживания этого центрального элемента я использую State, и каждый раз, когда этот State обновляется, происходит рекомпозиция. Я заметил в LayoutInspector, что ячейки в LazyRow постоянно обновляются при прокрутке влево или вправо. Этого не происходит, если я закомментирую отслеживание State.
Вопрос: как избежать ненужных рекомпозиций?
Вот код:
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.tween
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.Image
import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.lazy.LazyListItemInfo
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun JustRotator(
number: Int = 25,
cellSize: Dp = 75.dp
) {
val listState: LazyListState = rememberLazyListState()
LazyRow(
modifier = Modifier.fillMaxWidth(),
state = listState,
horizontalArrangement = Arrangement.Center,
flingBehavior = rememberSnapFlingBehavior(lazyListState = listState)
) {
items(count = number) { idx ->
val isCenterItem: Boolean = listState.isItemInCenter(idx)
val animatedSize: Dp by animateDpAsState(
targetValue = if (isCenterItem) (cellSize * 2) else cellSize,
animationSpec = tween(durationMillis = 300),
label = ""
)
Image(
modifier = Modifier
.size(animatedSize)
.clip(CircleShape),
painter = painterResource(id = R.drawable.ic_profile_chooser_lock),
contentDescription = null
)
}
}
}
@Composable
fun LazyListState.isItemInCenter(
idx: Int,
withOffset: Int = 0
): Boolean {
val isItemInCenter: Boolean by remember(this, idx) {
derivedStateOf {
val visibleItemsInfo: List<LazyListItemInfo> = layoutInfo.visibleItemsInfo
val screenCenter: Int = (layoutInfo.viewportStartOffset + layoutInfo.viewportEndOffset) / 2
val centeredItemIdx: Int = visibleItemsInfo.minByOrNull { itemInfo ->
val itemCenter: Int = (itemInfo.offset + itemInfo.size / 2)
kotlin.math.abs(screenCenter - itemCenter)
}?.index ?: -1
(centeredItemIdx + withOffset) == idx
}
}
return isItemInCenter
}