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

Jetpack Compose LazyColumn滚动严重卡顿问题求助

LazyColumn 滚动卡顿排查与优化方案

以下是针对你遇到的LazyColumn滚动卡顿问题的具体优化建议,覆盖未尝试的关键优化点:

1. 替换图片加载方式,启用缓存

painterResource未内置内存/磁盘缓存,列表滚动时会重复解码图片,占用大量UI线程资源。改用Jetpack推荐的Coil库加载图片:

  • 添加Coil依赖到模块级build.gradle.kts:
implementation("io.coil-kt:coil-compose:2.5.0")
  • 替换CardTask中的Image组件:
AsyncImage(
    model = ImageRequest.Builder(LocalContext.current)
        .data(task.imageRes)
        .crossfade(300)
        .build(),
    contentDescription = null,
    contentScale = ContentScale.FillWidth,
    modifier = Modifier
        .fillMaxWidth()
        .height(160.dp)
        .clip(RoundedCornerShape(12.dp))
)

2. 确保数据源稳定

检查tasks列表是否在每次Composable重组时被重新创建(比如直接在_30DaysApp内初始化),这会导致LazyColumn频繁重绘Item。将数据源缓存或托管到ViewModel:

@Composable
fun _30DaysApp() {
    // 用remember缓存数据源,避免每次重组重新生成
    val tasks = remember { preDefinedTasksList }
    Scaffold(topBar = { TopBar() }) { padding ->
        LazyColumn(
            contentPadding = padding,
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            itemsIndexed(tasks, key = { _, task -> task.id }) { index, task ->
                CardTask(task = task, day = index + 1)
            }
        }
    }
}

注:确保Task的id在列表初始化时只生成一次,不要每次创建Task实例时调用UUID.randomUUID()

3. 预加载自定义字体

滚动到展开的Item时才加载Poppins字体会导致UI线程阻塞,提前在Theme中预加载:

private val poppinsFontFamily = FontFamily(
    Font(R.font.poppins_regular, FontWeight.Normal)
)

@Composable
fun _30DaysTheme(
    darkTheme: Boolean = isSystemInDarkTheme(),
    dynamicColor: Boolean = true,
    content: @Composable () -> Unit
) {
    LaunchedEffect(Unit) {
        // 预加载字体,等待加载完成再渲染UI
        FontFamily.awaitLoad(poppinsFontFamily)
    }
    // 原有Theme逻辑...
}

4. 缩小重组范围,缓存静态数据

CardTask中的字符串资源每次重组都会重新解析,用remember缓存静态数据,减少不必要的重组:

@Composable
fun CardTask(task: Task, day: Int, modifier: Modifier = Modifier) {
    var expand by remember { mutableStateOf(false) }
    // 缓存静态字符串资源
    val dayText = remember(day) { stringResource(R.string.day, day) }
    val articleText = remember(task.articleRes) { stringResource(task.articleRes) }
    val descriptionText = remember(task.descriptionRes) { stringResource(task.descriptionRes) }

    Card(
        shape = RoundedCornerShape(16.dp),
        modifier = Modifier
            .padding(horizontal = 8.dp)
            .clickable { expand = !expand }
    ) {
        Column(
            modifier = modifier
                .animateContentSize(
                    animationSpec = spring(
                        dampingRatio = Spring.DampingRatioNoBouncy,
                        stiffness = Spring.StiffnessMedium
                    )
                )
                .padding(12.dp)
        ) {
            Row {
                Text(
                    text = dayText,
                    fontSize = 16.sp,
                    fontWeight = FontWeight.Bold
                )
                Spacer(modifier = Modifier.weight(1f))
                ExpandButton(expanded = expand, modifier = Modifier.size(24.dp))
            }
            Text(text = articleText, fontSize = 24.sp)
            Spacer(modifier = Modifier.size(8.dp))
            // 替换后的AsyncImage...
            if (expand) {
                Text(
                    text = descriptionText,
                    fontFamily = poppinsFontFamily,
                    modifier = Modifier.padding(top = 12.dp)
                )
            }
        }
    }
}

5. 滚动时暂停/调整动画

animateContentSize的动画在滚动时会占用UI线程资源,可在滚动时切换为更快速的动画:

@Composable
fun _30DaysApp() {
    val tasks = remember { preDefinedTasksList }
    val listState = rememberLazyListState()
    // 监听滚动状态
    val isScrolling = remember { derivedStateOf { listState.isScrollInProgress } }

    Scaffold(topBar = { TopBar() }) { padding ->
        LazyColumn(
            state = listState,
            contentPadding = padding,
            verticalArrangement = Arrangement.spacedBy(12.dp)
        ) {
            itemsIndexed(tasks, key = { _, task -> task.id }) { index, task ->
                CardTask(task = task, day = index + 1, isScrolling = isScrolling.value)
            }
        }
    }
}

// 修改CardTask参数,传入滚动状态
@Composable
fun CardTask(task: Task, day: Int, isScrolling: Boolean, modifier: Modifier = Modifier) {
    // ... 原有代码
    Column(
        modifier = modifier
            .animateContentSize(
                animationSpec = if (isScrolling) {
                    // 滚动时用高刚度动画,快速完成
                    spring(stiffness = Spring.StiffnessHigh)
                } else {
                    spring(
                        dampingRatio = Spring.DampingRatioNoBouncy,
                        stiffness = Spring.StiffnessMedium
                    )
                }
            )
            .padding(12.dp)
    ) {
        // ... 原有代码
    }
}

内容的提问来源于stack exchange,提问作者Pavlo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.24 21:48:10