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

Android Compose如何让LazyVerticalGrid行内项高度一致?

解决LazyVerticalGrid行内项等高问题

你的场景是网格项由图片+文本的Column组成,文本换行导致行内项高度不一致,且不想给所有项加不必要内边距来强制等高,以下是几种可行方案:

方案一:基于LazyListState动态计算行内最大高度

通过LazyListState获取每行的可见项,计算该行的最大高度,再给行内所有项统一设置这个高度。

@Composable
fun EqualHeightLazyGrid(items: List<Item>) {
    val listState = rememberLazyGridState()
    val rowMaxHeights = remember { mutableStateMapOf<Int, Int>() }

    LaunchedEffect(listState.layoutInfo) {
        // 遍历可见行,计算每行最大高度
        listState.layoutInfo.visibleItemsInfo.groupBy { it.row }.forEach { (rowIndex, items) ->
            val maxHeight = items.maxOfOrNull { it.size.height } ?: 0
            rowMaxHeights[rowIndex] = maxHeight
        }
    }

    LazyVerticalGrid(
        columns = GridCells.Fixed(2),
        state = listState
    ) {
        itemsIndexed(items) { index, item ->
            val rowIndex = listState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == index }?.row ?: 0
            val targetHeight = rowMaxHeights[rowIndex]

            Column(
                modifier = Modifier
                    .then(if (targetHeight != null) Modifier.height(targetHeight.dp) else Modifier)
                    .fillMaxWidth()
            ) {
                Image(
                    painter = painterResource(id = item.imageRes),
                    contentDescription = null,
                    modifier = Modifier.fillMaxWidth().aspectRatio(1f)
                )
                Text(text = item.text, modifier = Modifier.padding(top = 8.dp))
            }
        }
    }
}

data class Item(val imageRes: Int, val text: String)

方案二:使用SubcomposeLayout自定义等高网格

SubcomposeLayout允许先预测量所有子项的尺寸,再根据测量结果进行布局,能精准实现行内项等高,且不会产生多余内边距。

@Composable
fun EqualHeightGrid(
    columns: Int,
    items: List<Item>,
    modifier: Modifier = Modifier,
    rowSpacing: Dp = 8.dp
) {
    val rowSpacingPx = rowSpacing.toPx()
    SubcomposeLayout(modifier) { constraints ->
        val itemWidth = constraints.maxWidth / columns
        val itemConstraints = constraints.copy(minWidth = itemWidth)
        
        // 第一步:预测量所有项的高度
        val itemHeights = items.map { item ->
            subcompose("pre-measure-$item") {
                GridItem(item = item)
            }.first().measure(itemConstraints).height
        }
        
        // 按行分组计算每行最大高度
        val rowMaxHeights = itemHeights.chunked(columns).map { it.max() }
        // 计算整体布局高度
        val totalHeight = rowMaxHeights.sum() + (rowMaxHeights.size - 1) * rowSpacingPx.toInt()
        
        // 第二步:实际布局每个项
        layout(constraints.maxWidth, totalHeight) {
            items.forEachIndexed { index, item ->
                val row = index / columns
                val column = index % columns
                val targetHeight = rowMaxHeights[row]
                
                val placeable = subcompose("layout-$item") {
                    GridItem(item = item)
                }.first().measure(itemConstraints.copy(minHeight = targetHeight))
                
                val xOffset = column * itemWidth
                val yOffset = rowMaxHeights.take(row).sum() + (row * rowSpacingPx).toInt()
                placeable.place(x = xOffset, y = yOffset)
            }
        }
    }
}

@Composable
private fun GridItem(item: Item) {
    Column(modifier = Modifier.fillMaxWidth()) {
        Image(
            painter = painterResource(id = item.imageRes),
            contentDescription = null,
            modifier = Modifier.fillMaxWidth().aspectRatio(1f)
        )
        Text(text = item.text, modifier = Modifier.padding(top = 8.dp))
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 01:07:49