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
相关产品推荐
相关产品推荐

