滚动LazyRow时Box组件丢失圆角形状问题求助
滚动LazyRow时Box组件圆角丢失问题
滚动LazyRow时,移出可视区域的Box组件会丢失圆角形状。使用Modifier.clip(RoundedCornerShape(20.dp))修饰符后,组件在可视区域内时圆角正常,移出后形状消失,现象如下:

以下是对应的Composable函数代码:
@Composable fun ChartCaloriesSection( allCalorieData: List<DayCalorieData>, requiredCalorieAmount: Int ) { Surface( modifier = Modifier .fillMaxWidth() .height(210.dp) .padding(horizontal = 16.dp), color = MaterialTheme.colorScheme.surfaceVariant, shape = RoundedCornerShape(20.dp), shadowElevation = 2.dp ) { Column( modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.SpaceBetween ) { Text( text = "Calories statistics", modifier = Modifier.padding(16.dp) ) val basicFormatter = DateTimeFormatter.ofPattern("MMMM d, yyyy", Locale.ENGLISH) val sortedCalorieData = allCalorieData.sortedBy { LocalDate.parse(it.date, basicFormatter) } LazyRow( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp), contentPadding = PaddingValues( start = 16.dp, end = 16.dp ), modifier = Modifier.padding(bottom = 16.dp) ) { items(sortedCalorieData, key = { data -> data.date }) { data -> Column( horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.spacedBy(8.dp), modifier = Modifier .animateItem() ) { val uiFormatter = DateTimeFormatter.ofPattern("dd.MM") val date = LocalDate.parse(data.date, basicFormatter) val outputDateString = date.format(uiFormatter) var progress by rememberSaveable { mutableFloatStateOf(0f) } LaunchedEffect(key1 = requiredCalorieAmount, key2 = data.receivedCaloriesAmount) { progress = if(requiredCalorieAmount == 0) { 0f } else { (data.receivedCaloriesAmount - data.spentCaloriesAmount).toFloat() / requiredCalorieAmount.toFloat() } } val percentFromRequired by animateFloatAsState( targetValue = progress, label = "progress animation", animationSpec = tween(1000) ) Box( modifier = Modifier .weight(1f) .width(20.dp) .clip(RoundedCornerShape(20.dp)) .background(MaterialTheme.colorScheme.surfaceDim), contentAlignment = Alignment.BottomCenter, ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(percentFromRequired) .background( Brush.verticalGradient( colors = listOf( MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.secondary ) ) ) ) } Text( text = outputDateString, fontSize = 13.sp, ) } } } } } }
已尝试以下操作但未解决问题:
- 调整父Box的修饰符顺序
- 将
weight(1f)改为固定高度height(100.dp) - 移除Column的
Modifier.animateItem() - 为子Box添加clip修饰符
解决方案
这个问题本质是Lazy列表复用Item时,裁剪状态未正确同步或子组件背景超出裁剪范围导致的,可尝试以下方案:
方案1:给子Box添加同规格裁剪并调整修饰符顺序
确保子Box的clip修饰符在background之前,且与父Box使用相同的圆角参数,避免背景溢出:
Box( modifier = Modifier .weight(1f) .width(20.dp) .clip(RoundedCornerShape(20.dp)) .background(MaterialTheme.colorScheme.surfaceDim), contentAlignment = Alignment.BottomCenter, ) { Box( modifier = Modifier .fillMaxWidth() .fillMaxHeight(percentFromRequired) .clip(RoundedCornerShape(20.dp)) .background( Brush.verticalGradient( colors = listOf( MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.secondary ) ) ) ) }
方案2:用drawBehind替代嵌套Box
直接在父Box的drawBehind中绘制背景和进度条,统一受裁剪规则约束:
Box( modifier = Modifier .weight(1f) .width(20.dp) .clip(RoundedCornerShape(20.dp)) .drawBehind { // 绘制底部背景 drawRect(MaterialTheme.colorScheme.surfaceDim.toArgb()) // 绘制进度条 val progressHeight = size.height * percentFromRequired drawRect( brush = Brush.verticalGradient( colors = listOf( MaterialTheme.colorScheme.primary, MaterialTheme.colorScheme.secondary ) ), size = Size(size.width, progressHeight), topLeft = Offset(0f, size.height - progressHeight) ) }, contentAlignment = Alignment.BottomCenter, )
方案3:优化进度状态的记忆逻辑
替换rememberSaveable为remember,并绑定Item的唯一标识,确保复用Item时状态正确初始化:
// 替换原有progress声明和LaunchedEffect var progress by remember(data.date, requiredCalorieAmount) { mutableFloatStateOf( if(requiredCalorieAmount == 0) 0f else (data.receivedCaloriesAmount - data.spentCaloriesAmount).toFloat() / requiredCalorieAmount.toFloat() ) } val percentFromRequired by animateFloatAsState( targetValue = progress, label = "progress animation", animationSpec = tween(1000) )
内容的提问来源于stack exchange,提问作者Егор Сауткин
相关产品推荐
相关产品推荐

