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

Jetpack Compose LazyRow实现自适应子项宽度需求问询

Jetpack Compose LazyRow实现自适应子项宽度需求问询

Hey there! This is such a common and practical requirement—LazyRow doesn’t natively support weight distribution like a regular Row, but we can build a workaround that dynamically adjusts item widths based on whether the content fits the screen. Let’s break this down step by step:

Core Logic

The key is to:

  1. Get the total available width of the LazyRow’s parent container
  2. Calculate how much space all items would take if they only used their content width
  3. If the total content width is less than the parent width, split the parent width equally among items
  4. If content exceeds the parent width, let each item use its natural content width so the row becomes scrollable

Implementation Code

@Composable
fun AdaptiveLazyRow(
    items: List<String>,
    modifier: Modifier = Modifier,
    itemSpacing: Dp = 8.dp,
    itemContent: @Composable (String) -> Unit
) {
    var parentWidth by remember { mutableStateOf(0) }
    var itemMinWidths by remember { mutableStateOf<List<Int>>(emptyList()) }

    Box(modifier = modifier.onSizeChanged { parentWidth = it.width }) {
        SubcomposeLayout { constraints ->
            // Step 1: Pre-measure each item's minimal intrinsic width
            val itemMeasurables = items.map { 
                subcompose(it) { itemContent(it) }.first() 
            }
            val itemMinWidthList = itemMeasurables.map { 
                it.minIntrinsicWidth(constraints.maxHeight) 
            }
            itemMinWidths = itemMinWidthList

            // Step 2: Calculate total required space for content-only widths
            val totalContentWidth = itemMinWidthList.sum() + (itemSpacing.toPx() * (items.size - 1)).toInt()

            // Step 3: Compose the LazyRow with dynamic item widths
            val lazyRowMeasurable = subcompose("LazyRow") {
                LazyRow(
                    modifier = Modifier.fillMaxWidth(),
                    horizontalArrangement = Arrangement.spacedBy(itemSpacing)
                ) {
                    itemsIndexed(items) { index, item ->
                        val targetWidth = if (totalContentWidth <= parentWidth) {
                            // Equal width distribution (subtract spacing first)
                            (parentWidth - (itemSpacing.toPx() * (items.size - 1)).toInt()) / items.size
                        } else {
                            // Use item's natural content width
                            itemMinWidthList[index]
                        }

                        Box(modifier = Modifier.width(targetWidth.toDp())) {
                            itemContent(item)
                        }
                    }
                }
            }.first()

            // Measure and place the LazyRow
            val placeable = lazyRowMeasurable.measure(constraints)
            layout(placeable.width, placeable.height) {
                placeable.place(0, 0)
            }
        }
    }
}

How This Works

  1. Parent Width Tracking: The outer Box uses onSizeChanged to capture the full width available to the LazyRow.
  2. Item Width Measurement: SubcomposeLayout lets us pre-measure each item’s minimal width (the smallest space it needs to display content without wrapping).
  3. Dynamic Width Calculation: We compare the total content width (plus spacing) to the parent width. If content fits, we split the parent width equally; if not, we use each item’s natural width.
  4. Dynamic Item Support: The remember state variables ensure that whenever the items list changes, the system re-measures and recalculates widths automatically.

Usage Example

@Composable
fun AdaptiveRowDemo() {
    var items by remember { mutableStateOf(listOf("Short", "Medium Text", "Longer Item Example")) }

    Column(modifier = Modifier.fillMaxSize().padding(16.dp)) {
        AdaptiveLazyRow(items = items) { text ->
            Column(
                modifier = Modifier.padding(8.dp),
                horizontalAlignment = Alignment.CenterHorizontally
            ) {
                Text(
                    text = text,
                    style = MaterialTheme.typography.labelMedium,
                    textAlign = TextAlign.Center
                )
                HorizontalDivider(modifier = Modifier.padding(4.dp))
                Text("Subtext")
            }
        }

        Button(
            onClick = { items = items + "New Item ${items.size + 1}" },
            modifier = Modifier.padding(top = 16.dp)
        ) {
            Text("Add Item")
        }
    }
}

Quick Notes

  • Adjust the itemSpacing parameter to match your desired gap between items—this value is accounted for in the equal-width calculation to avoid overflow.
  • If your items have variable heights, you can extend this logic with intrinsic height measurements, but this example focuses on your core width requirement.
  • The SubcomposeLayout ensures efficient measurement, so performance stays smooth even with frequent item list changes.

Hope this solves your problem perfectly! Feel free to tweak the code to fit your specific UI needs.

备注:内容来源于stack exchange,提问作者AlexEZST

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.16 11:18:08