如何避免Jetpack Compose中LazyRow的不必要重组
优化LazyRow不必要重组的方案
问题根源
当前实现中,每个LazyRow的Item都会单独调用isItemInCenter计算自身是否居中,而这个函数内部的derivedStateOf依赖LazyListState.layoutInfo——滚动时layoutInfo会持续更新,导致所有可见Item都触发重组。即使只有居中项需要改变大小,其他无关Item也会跟着刷新,造成不必要的性能开销。
具体优化步骤
1. 全局计算居中项索引,避免每个Item重复计算
将居中项的计算逻辑从单个Item中抽离,在LazyRow外部统一计算当前居中的索引,这样只有当居中索引变化时,才会触发相关Item的重组,而非所有可见Item。
修改后的核心代码:
@OptIn(ExperimentalFoundationApi::class) @Composable fun JustRotator( number: Int = 25, cellSize: Dp = 75.dp ) { val listState: LazyListState = rememberLazyListState() // 全局计算当前居中的索引,仅当layoutInfo变化且居中项改变时更新 val centeredItemIdx by remember(listState) { derivedStateOf { val visibleItemsInfo = listState.layoutInfo.visibleItemsInfo if (visibleItemsInfo.isEmpty()) return@derivedStateOf -1 val screenCenter = (listState.layoutInfo.viewportStartOffset + listState.layoutInfo.viewportEndOffset) / 2 visibleItemsInfo.minByOrNull { itemInfo -> val itemCenter = itemInfo.offset + itemInfo.size / 2 kotlin.math.abs(screenCenter - itemCenter) }?.index ?: -1 } } LazyRow( modifier = Modifier.fillMaxWidth(), state = listState, horizontalArrangement = Arrangement.Center, flingBehavior = rememberSnapFlingBehavior(lazyListState = listState) ) { items(count = number) { idx -> // 直接对比全局计算的索引,避免每个Item重复计算 val isCenterItem = idx == centeredItemIdx RotatorItem(isCenterItem = isCenterItem, cellSize = cellSize) } } }
2. 将Item内容封装为独立稳定Composable
把单个Item的UI逻辑提取成独立的Composable,利用Compose的稳定特性,只有当输入参数(isCenterItem和cellSize)变化时才会重组。
@Composable private fun RotatorItem(isCenterItem: Boolean, cellSize: Dp) { val animatedSize: Dp by animateDpAsState( targetValue = if (isCenterItem) (cellSize * 2) else cellSize, animationSpec = tween(durationMillis = 300), label = "item_size_animation" ) Image( modifier = Modifier .size(animatedSize) .clip(CircleShape), painter = painterResource(id = R.drawable.ic_profile_chooser_lock), contentDescription = "profile icon" ) }
3. 移除原有的isItemInCenter扩展函数
原扩展函数每个Item都会创建一个derivedStateOf,现在全局计算后该函数已无用,可以直接删除。
优化效果
- 滚动时仅居中项和之前的居中项会触发重组(因为它们的
isCenterItem状态变化),其他可见Item不再无意义刷新。 - 减少了多个
derivedStateOf的创建,降低状态管理的开销。 - 布局检查器中不会再出现所有Item持续更新的现象。
内容的提问来源于stack exchange,提问作者Sirop4ik
相关产品推荐
相关产品推荐

