Jetpack Compose BottomSheetScaffold中LazyColumn过度渲染占用额外空间问题
Jetpack Compose BottomSheet内LazyColumn过度渲染问题解决
问题场景
使用Jetpack Compose开发Android应用,先后尝试了Morfly Advanced Bottom Sheet(基于Material3 BottomSheetScaffold实现)和官方Material3 BottomSheetScaffold,均遇到同一问题:底部弹窗内的LazyColumn会渲染远超可见区域的列表项。实际屏幕仅能显示5个可见项,但通过scrollState.layoutInfo.visibleItemsInfo日志排查发现,已有17个项被提前渲染,引发性能问题。
尝试过调整修饰符、高度约束均无效;给LazyColumn设置固定尺寸可解决过度渲染,但会导致底部弹窗无法按需展开。
原代码如下:
@OptIn(ExperimentalMaterial3Api::class) @Composable fun LeagueScreen(viewModel: LeagueScreenViewModel) { val scrollState = rememberLazyListState() val tops = UtilsKt.getMockTops(20) LaunchedEffect(scrollState) { snapshotFlow { scrollState.layoutInfo.visibleItemsInfo } .distinctUntilChanged() .collect { visibleItems -> Utils.log("visibleItemIndices: ${visibleItems.map { it.key }}") } } val scaffoldState = io.morfly.compose.bottomsheet.material3.rememberBottomSheetScaffoldState() io.morfly.compose.bottomsheet.material3.BottomSheetScaffold( sheetShape = RoundedCornerShape(16.dp), scaffoldState = scaffoldState, sheetContent = { LazyColumn( modifier = Modifier.fillMaxWidth(), state = scrollState ) { items(count = tops.size) { index -> PeopleOnLeagueItem( item = tops[index], onItemClick = { }, modifier = Modifier ) } } }, content = {} ) }
问题原因
BottomSheetScaffold的sheetContent默认会向父布局请求最大可用高度,即使BottomSheet处于部分展开状态,布局测量阶段仍会按照全展开的高度来计算。LazyColumn在没有明确的最大高度约束时,会基于这个最大可用高度预渲染大量列表项,导致过度渲染。
解决方案
通过动态约束LazyColumn的最大高度,使其与BottomSheet当前的实际可见高度保持一致,这样LazyColumn只会渲染当前可见区域内(及少量预加载)的项,同时不影响BottomSheet的按需展开功能。
修改后的代码
@OptIn(ExperimentalMaterial3Api::class) @Composable fun LeagueScreen(viewModel: LeagueScreenViewModel) { val scrollState = rememberLazyListState() val tops = UtilsKt.getMockTops(20) val scaffoldState = io.morfly.compose.bottomsheet.material3.rememberBottomSheetScaffoldState() // 存储BottomSheet当前的实际高度 val currentSheetHeight = remember { mutableStateOf(0.dp) } LaunchedEffect(scrollState) { snapshotFlow { scrollState.layoutInfo.visibleItemsInfo } .distinctUntilChanged() .collect { visibleItems -> Utils.log("visibleItemIndices: ${visibleItems.map { it.key }}") } } io.morfly.compose.bottomsheet.material3.BottomSheetScaffold( sheetShape = RoundedCornerShape(16.dp), scaffoldState = scaffoldState, sheetContent = { Box( modifier = Modifier .fillMaxWidth() // 监听BottomSheet的实际绘制尺寸,更新高度 .onSizeChanged { size -> currentSheetHeight.value = size.height.toDp() } ) { LazyColumn( modifier = Modifier .fillMaxWidth() // 动态约束LazyColumn的最大高度 .heightIn(max = currentSheetHeight.value), state = scrollState ) { items(count = tops.size) { index -> PeopleOnLeagueItem( item = tops[index], onItemClick = { }, modifier = Modifier ) } } } }, content = {} ) }
关键说明
- 动态高度约束:通过
Box的onSizeChanged监听BottomSheet的实际绘制高度,实时更新currentSheetHeight,再用heightIn(max = currentSheetHeight.value)约束LazyColumn的最大高度,确保LazyColumn仅基于当前可见高度计算渲染项。 - 兼容性:如果切换到官方Material3的
BottomSheetScaffold,只需替换对应的包名(将io.morfly.compose.bottomsheet.material3改为androidx.compose.material3),代码逻辑完全通用。 - 性能保障:这种方式既保留了BottomSheet按需展开的特性,又避免了LazyColumn过度渲染,同时
onSizeChanged的开销极低,不会影响整体性能。
内容的提问来源于stack exchange,提问作者Amin
相关产品推荐
相关产品推荐

