You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何避免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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 14:37:03