Jetpack Compose:所有项目高度相同的行

问题描述 投票:0回答:10

我正在尝试在 Compose 中实现一个布局,其中水平滚动的

Row
的项目都应具有相同的高度,因此较小的项目应调整为行中最大项目的大小。我知道“内在尺寸”,但我就是无法让它发挥作用。另外,我不想为 Row 分配固定高度,因为 Row 的高度也应该是其最大子可组合项的高度。
这是简化的布局

@Composable fun Screen( modifier: Modifier = Modifier, ) { Row( modifier = modifier .height(IntrinsicSize.Min) .horizontalScroll(state = rememberScrollState()), horizontalArrangement = Arrangement.spacedBy(10.dp), ) { Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy " + "eirmod tempor invidunt ut labore et dolore magna aliquyam" ) Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy " + "eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam " + "voluptua. At" ) Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam" ) } } @Composable private fun Item( modifier: Modifier = Modifier, text: String, ) { Column( modifier = modifier.width(200.dp), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.SpaceBetween ) { Column { Text("Some static text") // Dynamic text Text( text, modifier = Modifier.padding(top = 5.dp) ) } // The space between these two composables should be flexible, // hence the outer column with Arrangement.SpaceBetween Button( modifier = Modifier.padding(top = 20.dp), onClick = {} ) { Text("Button") } } }

这就是结果

但我真正想要的是

当我将

fillMaxHeight()

应用于

Item
时,这些项目占据整个高度,并且所有按钮都与屏幕底部对齐。
Jetpack Compose 版本:1.1.0

更新:

这是 Compose 中的一个错误,已在 compose-foundation 版本 1.3.0-beta01

修复

android android-jetpack-compose android-jetpack
10个回答
29
投票

自 Compose v1.3.0 起,惰性列表不支持

modifier.height(intrinsicSize = IntrinsicSize.Max)

所以现在我们必须将 Row 与 

modifier = modifier.height(intrinsicSize = IntrinsicSize.Max)

 一起使用
编写 RowItem 时添加

Spacer(modifier = Modifier.weight(1f))

来填充空白空间

@Composable
fun RowItem(modifier: Modifier = Modifier) {
Surface(
    modifier = modifier,
    color = Color.Gray,
    shape = RoundedCornerShape(12.dp)
) {
    Column(
        verticalArrangement = Arrangement.spacedBy(12.dp),
        modifier = Modifier.padding(16.dp)
    ) {
        repeat((1..4).random()) {
            Text(
                text = "item $it",
                color = Color.White,
                modifier = Modifier.padding(
                    horizontal = 16.dp,
                    vertical = 4.dp
                )
            )
        }

        Spacer(modifier = Modifier.weight(1f)) // this is required to push below composables to bottom

        Button(onClick = {
        }) { Text(text = "Button") }
    }
}}

确保添加 
horizontalScroll(rememberScrollState())

以使行可滚动,并添加

height(intrinsicSize = IntrinsicSize.Max)
以使所有卡片的高度达到最高的项目。
@Composable
fun ScrollableRow() {
Row(
    Modifier
        .horizontalScroll(rememberScrollState()) // this makes it scrollable
        .height(intrinsicSize = IntrinsicSize.Max) // this make height of all cards to the tallest card.
        .padding(horizontal = 16.dp),
    content = {
        repeat(4) {
            RowItem(modifier = Modifier.fillMaxHeight())
        }
    },
    horizontalArrangement = Arrangement.spacedBy(16.dp),
)}

结果:


6
投票

fun Modifier.minimumHeightModifier(state: MinimumHeightState, density: Density) = onSizeChanged { size -> val itemHeight = with(density) { val height = size.height height.toDp() } if (itemHeight > state.minHeight ?: 0.dp) { state.minHeight = itemHeight } }.defaultMinSize(minHeight = state.minHeight ?: Dp.Unspecified) class MinimumHeightState(minHeight: Dp? = null) { var minHeight by mutableStateOf(minHeight) }

然后,您可以配置修改器并将其应用到您想要具有相同最小高度的所有内容

val density = LocalDensity.current val minimumHeightState = remember { MinimumHeightState() } val minimumHeightStateModifier = Modifier.minimumHeightModifier( minimumHeightState, density )

这些都在 LazyRow 中

itemsIndexed(items = carouselModel.subviews, key = { _, item -> item.id }) { _, item -> when (item) { is BasicCard -> { FooCard( modifier = minimumHeightStateModifier, section = item, onAction = onAction ) } is BarCard -> { BarVerticalCard( modifier = minimumHeightStateModifier, section = item, onAction = onAction ) }

有关 kotlin slack 解决方案的讨论可以在这里找到:
https://kotlinlang.slack.com/archives/CJLTWPH7S/p1649956718414129


3
投票
SubComposeLayout

实现这样的功能来设置每个元素的高度,它可以让您根据新的指标(例如具有最大宽度或高度的同级元素)重新测量可组合项。

您可以检查 SubComposeLayout 的描述

这里

,或者我的答案,让列具有相等的宽度这里 @Composable fun SubcomposeRow( modifier: Modifier = Modifier, content: @Composable () -> Unit = {}, ) { SubcomposeLayout(modifier = modifier) { constraints -> var recompositionIndex = 0 var placeables: List<Placeable> = subcompose(recompositionIndex++, content).map { it.measure(constraints) } placeables.forEachIndexed() { index: Int, placeable: Placeable -> println("Index: $index, placeable width: ${placeable.width}, height: ${placeable.height}") } var rowSize = placeables.fold(IntSize.Zero) { currentMax: IntSize, placeable: Placeable -> IntSize( width = currentMax.width + placeable.width, height = maxOf(currentMax.height, placeable.height) ) } // Remeasure every element using height of longest item as minHeight of Constraint if (!placeables.isNullOrEmpty() && placeables.size > 1) { placeables = subcompose(recompositionIndex, content).map { measurable: Measurable -> measurable.measure( Constraints( minHeight = rowSize.height, maxHeight = constraints.maxHeight ) ) } rowSize = placeables.fold(IntSize.Zero) { currentMax: IntSize, placeable: Placeable -> IntSize( width = currentMax.width + placeable.width, height = maxOf(currentMax.height, placeable.height) ) } } layout(rowSize.width, rowSize.height) { var xPos = 0 placeables.forEach { placeable: Placeable -> placeable.placeRelative(xPos, 0) xPos += placeable.width } } } }

第二次测量中的约束很重要,因为我们希望每个可组合项都具有最大高度

Constraints( minHeight = rowSize.height, maxHeight = constraints.maxHeight )

使用方法

@Composable fun Screen( modifier: Modifier = Modifier, ) { SubcomposeRow( modifier = modifier .background(Color.LightGray) .horizontalScroll(state = rememberScrollState()), ) { Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy " + "eirmod tempor invidunt ut labore et dolore magna aliquyam" ) Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy " + "eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam " + "voluptua. At" ) Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam" ) } } @Composable private fun Item( modifier: Modifier = Modifier, text: String, ) { Column( modifier = modifier .width(200.dp) .background(Color.Red), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.SpaceBetween ) { Column(modifier = Modifier.background(Color.Yellow)) { Text("Some static text") // Dynamic text Text( text, modifier = Modifier.padding(top = 5.dp) ) } // The space between these two composables should be flexible, // hence the outer column with Arrangement.SpaceBetween Button( modifier = Modifier .padding(top = 20.dp), onClick = {} ) { Text("Button") } } }

结果


2
投票

@Preview @Composable fun PreviewTest() { Row(Modifier.height(IntrinsicSize.Min)) { Box( modifier = Modifier .background(Color.Red) .size(size = 80.dp) ) Box( modifier = Modifier .background(Color.Green) .defaultMinSize(minWidth = 80.dp, minHeight = 40.dp) .fillMaxHeight() ) Box( modifier = Modifier .background(Color.Blue) .defaultMinSize(minWidth = 80.dp, minHeight = 40.dp) .fillMaxHeight() ) } }


1
投票
报告

。它于 2022 年 8 月 13 日标记为已修复。但是尚不清楚哪个 Compose 版本将包含此修复。

更新:

该错误已在compose-foundation版本

1.3.0-beta01
中修复。


0
投票
onTextLayout

中使用了

Text()
onTextLayout = { result ->
 val offsetLines = result.multiParagraph.maxLines - 
                   result.multiParagraph.lineCount
 if (offsetLines > 0)
     text = text.plus("/n".repeat(offsetLines))
 }

找到最小行数并添加空间,直到购物车中的所有文本都具有相同的高度。


0
投票

@Preview @Composable fun Screen( modifier: Modifier = Modifier, ) { Row( modifier = modifier //**SET HEIGHT TO INSTRINSICSIZE.MAX** .height(IntrinsicSize.Max) .horizontalScroll(state = rememberScrollState()), horizontalArrangement = Arrangement.spacedBy(10.dp), ) { Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy " + "eirmod tempor invidunt ut labore et dolore magna aliquyam" ) Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy " + "eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam " + "voluptua. At" ) Item( text = "Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam" ) } } @Composable private fun Item( modifier: Modifier = Modifier, text: String, ) { Column( modifier = modifier.width(200.dp), horizontalAlignment = Alignment.End, verticalArrangement = Arrangement.SpaceBetween ) { Column { Text("Some static text") // Dynamic text Text( text, modifier = Modifier.padding(top = 5.dp) ) } //**THIS IS THE TRICK** Spacer(modifier = Modifier.weight(1f)) // The space between these two composables should be flexible, // hence the outer column with Arrangement.SpaceBetween Button( modifier = Modifier.padding(top = 20.dp), onClick = {} ) { Text("Button") } } }

see resulting screenshot


0
投票

val rowHeight = with(LocalDensity.current) { lazyRowtState.layoutInfo.viewportSize.height.toDp() }

然后只需设置孩子的高度即可。


0
投票
LazyLists

的,但可能可以进行调整以适用于任何

LazyVerticalGrid
本质上,使用
LazyList
我们可以找到每行中项目的最大高度,并将其设置为该行中每个项目的高度。

visibleItemsInfo

我不确定这是否在撰写过程中做了太多工作,或者违反了某种撰写规则,但它似乎对我有用,而且我没有遇到任何问题。


我使用了有点混乱(按照我的标准)的解决方案来解决

val state = rememberLazyGridState() val density = LocalDensity.current LazyVerticalGrid( columns = GridCells.Adaptive(minSize = 150.dp), state = state ) { itemsIndexed(myItemStates) { index, myItemState -> val row = state.layoutInfo.visibleItemsInfo.find { it.index == index }?.row val itemsInRow = state.layoutInfo.visibleItemsInfo.filter { it.row == row } val maxHeightInRow = itemsInRow.maxOfOrNull { it.size.height } val maxHeightInRowDp = with(density) { maxHeightInRow?.toDp() } ?: Dp.Unspecified MyItem( state = myItemState, modifier = Modifier.height(maxHeightInRowDp) ) } }

0
投票
LazyRow

Row
中的虚拟元素的组合,用于计算所有元素的 minHeight。
Modifier.onPlaced
希望它能帮助别人。

© www.soinside.com 2019 - 2024. All rights reserved.