Jetpack Compose:如何在LazyColumn中实现展开显列表的可折叠卡片
解决Jetpack Compose LazyColumn中可折叠卡片的滚动联动问题
我在Jetpack Compose的LazyColumn中添加可折叠卡片时遇到了问题:当卡片展开显示章节列表时,上方的卡片无法像LazyColumn的正常项一样滚出屏幕。
问题根源
当前实现的核心问题是在ExpandableCard内部嵌套了LazyColumn,这导致章节列表的滚动被限制在卡片内部,父LazyColumn只会将整个ExpandableCard视为一个固定高度的项,无法联动滚动让上方卡片滚出视野。
解决方案
将所有内容(卡片头部+展开的章节)整合到同一个父LazyColumn中,利用LazyColumn的动态item特性实现折叠/展开,避免嵌套滚动组件。
修改步骤与代码
1. 定义主题状态类
用于管理每个主题的展开状态:
data class SubjectState(val subject: Subjects, var isExpanded: Boolean = false)
2. 初始化状态列表
在父Composable中创建并维护所有主题的状态:
val subjectsList = listOf( PhysicsMechanics, PhysicsThermodynamics, PhysicsSound, PhysicsOptics, PhysicsElectrostatics, PhysicsElectricityAndMagnetism, PhysicsModernPhysics ) val subjectStates = remember { subjectsList.map { SubjectState(it) }.toMutableStateList() }
3. 重构父LazyColumn
将卡片头部和展开的章节统一作为父LazyColumn的item:
LazyColumn { subjectStates.forEachIndexed { index, state -> // 卡片头部项 item(key = state.subject.name) { ExpandableCardHeader( subject = state.subject, isExpanded = state.isExpanded, onToggle = { subjectStates[index] = state.copy(isExpanded = !state.isExpanded) } ) Spacer(modifier = Modifier.height(2.dp)) } // 展开时显示章节项 if (state.isExpanded) { items(state.subject.chapters, key = { it.name }) { chapter -> ChapterBox(chapterName = chapter) Spacer(modifier = Modifier.height(10.dp)) } } } }
4. 拆分卡片头部组件
将原ExpandableCard拆分为仅负责头部交互的组件:
@OptIn(ExperimentalMaterial3Api::class) @Composable fun ExpandableCardHeader( subject: Subjects, isExpanded: Boolean, onToggle: () -> Unit, modifier: Modifier = Modifier ) { val rotationState by animateFloatAsState( targetValue = if (isExpanded) 180f else 0f, label = "" ) Card( modifier = modifier .fillMaxWidth() .animateContentSize( animationSpec = tween( durationMillis = 300, easing = LinearOutSlowInEasing ) ), shape = RoundedCornerShape(8.dp), onClick = onToggle ) { Row( horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically, modifier = Modifier .padding(12.dp) .fillMaxWidth() ) { Text( text = subject.name, style = MaterialTheme.typography.titleMedium ) IconButton( modifier = Modifier.rotate(rotationState), onClick = onToggle ) { Icon( imageVector = Icons.Default.ArrowDropDown, contentDescription = "展开/折叠章节" ) } } } }
5. 保留原有章节项组件
ChapterBox无需修改,直接复用:
@Composable fun ChapterBox( modifier: Modifier = Modifier, chapterName: ChapterName ) { Box( modifier = modifier .fillMaxSize() .padding(2.dp) .background(color = Color(0xFF8F6F5FA), RoundedCornerShape(8.dp)) ) { Row( verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(16.dp), modifier = Modifier .fillMaxWidth() .padding(16.dp) ) { Image(painter = painterResource(id = chapterName.icon), contentDescription = null) Column { Text(text = chapterName.name, maxLines = 1, overflow = TextOverflow.Ellipsis) Spacer(Modifier.height(4.dp)) Text( text = chapterName.description, style = MaterialTheme.typography.bodyMedium, maxLines = 1, overflow = TextOverflow.Ellipsis ) } } } }
效果说明
修改后,所有内容都在同一个LazyColumn中,展开主题时,章节会作为动态item插入到列表中,整个列表的滚动是全局联动的,上方的卡片可以像普通LazyColumn项一样正常滚出屏幕。
内容的提问来源于stack exchange,提问作者Hari Joshi
相关产品推荐
相关产品推荐

