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

如何为API返回的JSON数据实现分组LazyColumn列表

嵌套LazyColumn滚动失效问题解决(List of Lists of Objects分组展示)

问题背景

需要将API返回的List of Lists of Objects格式数据在LazyColumn中分组展示,当前实现因嵌套LazyColumn导致列表无法正常滚动,相关代码及数据格式如下:

问题代码示例

@Composable
fun CupStandingExpandableGroup(
    mainViewModel: MainViewModel = hiltViewModel(),
    standingsItemList: List<StandingsItemItem?>,
    standingsItemListOfList: List<List<StandingsItemItem?>?>?,
    headerItem: @Composable ()-> Unit,
    keys: @Composable ()-> Unit
) {
    var expandedState by remember { mutableStateOf(true) }
    val rotationState by animateFloatAsState(
        targetValue = if (expandedState) 180f else 0f, label = ""
    )
    Column {
        standingsItemListOfList!!.forEach {
            mainViewModel.standingsListTournament.value = it!!
            val groupedStandingsList = mainViewModel.standingsListTournament.value.groupBy { it!!.group }
            LazyColumn {
                item { headerItem() }
                groupedStandingsList.forEach { (group, items) ->
                    item {
                        Card(
                            modifier = Modifier
                                .fillMaxWidth()
                                .animateContentSize(
                                    animationSpec = tween(
                                        durationMillis = 3000,
                                        easing = LinearOutSlowInEasing
                                    )
                                ),
                            shape = RoundedCornerShape(15.dp),
                            onClick = {
                                expandedState = !expandedState
                            }
                        ) {
                            Column(
                                modifier = Modifier
                                    .fillMaxWidth()
                                    .padding(4.dp)
                            ) {
                                Row(
                                    verticalAlignment = Alignment.CenterVertically
                                ) {
                                    Text(
                                        modifier = Modifier
                                            .weight(6f),
                                        text = group!!,
                                        fontSize = 13.sp,
                                        fontWeight = FontWeight.SemiBold,
                                        maxLines = 1,
                                        overflow = TextOverflow.Ellipsis
                                    )
                                    IconButton(
                                        modifier = Modifier
                                            .weight(1f)
                                            .alpha(ContentAlpha.medium)
                                            .rotate(rotationState),
                                        onClick = {
                                            expandedState = !expandedState
                                        }) {
                                        Icon(
                                            imageVector = Icons.Default.ArrowDropDown,
                                            contentDescription = "Drop-Down Arrow"
                                        )
                                    }
                                }
                            }
                        }
                    }
                    if (expandedState) {
                        itemsIndexed(
                            items,
                            itemContent = { index, item ->
                                StandingRow(standingsItem = item!!)
                            }
                        )
                    }
                }
                item { keys() }
            }
        }
    }
}

List of Lists of Objects数据格式示例

[
   [
      {
      },
      {
      }
   ],
   [
      {
      },
      {
      }
   ],
   [
      {
      },
      {
      }
   ]
]

单列表数据的正常分组实现示例(对比参考)

val list = viewModel.mainList.value.groupBy { it!!.league!!.id }
if (myList.isNotEmpty()) {
    LazyColumn(
        modifier = Modifier
            .padding(3.dp)
            .fillMaxSize()
    ) {
        myList.forEach { (league, items) ->
            item {
                TodayMatchesHeader(leagueItem = items[0]!!, league = league!!)
            }
            itemsIndexed(items,
                itemContent = { index, item ->
                    TodayMatchesRow(
                        item,
                        onClick = {
                            // 点击事件逻辑
                        }
                    )
                }
            )
        }
    }
}

List of Objects数据格式示例(对比参考)

[
   {
   },
   {
   },
   {
   }
]

问题分析

  1. 嵌套LazyColumn无高度限制:外层Column循环创建多个LazyColumn,每个LazyColumn默认会占据尽可能多的高度,导致滚动事件冲突,无法正常滚动。
  2. 全局展开状态错误:所有分组共用同一个expandedState,点击任意分组的展开按钮会同时控制所有分组的展开/收起,不符合预期。

解决方案

核心修改点

  • 移除嵌套的LazyColumn,改用单个根LazyColumn承载所有内容,避免滚动冲突。
  • 为每个分组单独维护展开状态,而不是使用全局状态。
  • 直接处理standingsItemListOfList数据,无需借助ViewModel临时存储,简化逻辑。

修正后的代码

@Composable
fun CupStandingExpandableGroup(
    standingsItemListOfList: List<List<StandingsItemItem?>?>?,
    headerItem: @Composable ()-> Unit,
    keys: @Composable ()-> Unit
) {
    // 过滤掉空列表和空元素,简化后续处理
    val validData = standingsItemListOfList?.filterNotNull()?.flatten()?.filterNotNull() ?: emptyList()
    // 按group分组
    val groupedStandingsList = validData.groupBy { it.group }

    LazyColumn(
        modifier = Modifier.fillMaxSize()
    ) {
        // 添加头部
        item { headerItem() }

        // 遍历每个分组
        groupedStandingsList.forEach { (group, items) ->
            // 为每个分组单独维护展开状态
            var expandedState by remember { mutableStateOf(true) }
            val rotationState by animateFloatAsState(
                targetValue = if (expandedState) 180f else 0f, label = ""
            )

            // 分组头部卡片
            item {
                Card(
                    modifier = Modifier
                        .fillMaxWidth()
                        .animateContentSize(
                            animationSpec = tween(
                                durationMillis = 300, // 缩短动画时长更自然
                                easing = LinearOutSlowInEasing
                            )
                        ),
                    shape = RoundedCornerShape(15.dp),
                    onClick = { expandedState = !expandedState }
                ) {
                    Column(
                        modifier = Modifier
                            .fillMaxWidth()
                            .padding(4.dp)
                    ) {
                        Row(verticalAlignment = Alignment.CenterVertically) {
                            Text(
                                modifier = Modifier.weight(6f),
                                text = group ?: "未分组",
                                fontSize = 13.sp,
                                fontWeight = FontWeight.SemiBold,
                                maxLines = 1,
                                overflow = TextOverflow.Ellipsis
                            )
                            IconButton(
                                modifier = Modifier
                                    .weight(1f)
                                    .alpha(ContentAlpha.medium)
                                    .rotate(rotationState),
                                onClick = { expandedState = !expandedState }
                            ) {
                                Icon(
                                    imageVector = Icons.Default.ArrowDropDown,
                                    contentDescription = "展开/收起"
                                )
                            }
                        }
                    }
                }
            }

            // 展开时显示分组内的列表项
            if (expandedState) {
                itemsIndexed(items) { _, item ->
                    StandingRow(standingsItem = item)
                }
            }
        }

        // 添加底部的keys
        item { keys() }
    }
}

额外优化说明

  1. 数据预处理:先过滤掉空列表和空元素,避免后续频繁处理null值,提升代码健壮性。
  2. 独立展开状态:每个分组通过remember单独维护expandedState,确保操作互不影响。
  3. 简化动画时长:将动画时长从3000ms缩短到300ms,交互更流畅自然。
  4. 移除不必要的ViewModel依赖:直接在Composable内处理数据,无需借助ViewModel临时存储,减少耦合。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 21:59:55