android Jetpack组成:所有项高度相同的行

brc7rcf0  于 2023-03-06  发布在  Android
关注(0)|答案(7)|浏览(156)

我尝试在Compose中实现一个布局,其中可水平滚动的Row的项目应该都具有相同的高度,因此较小的项目应该调整为行中最大项目的大小。我知道intrinsic size,但我就是不能让它工作。而且我不想为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合成版本:1.1.0

**更新:**这是合成中的错误,在compose-foundation版本1.3.0-beta 01中为fixed

9ceoxa92

9ceoxa921#

此方法仅适用于列表很小的情况,因为Row()一次加载所有项(而不是延迟加载)
自合成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())以使Row可滚动,添加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),
)}

结果:

m1m5dgzv

m1m5dgzv2#

我创建了一个Google不推荐的解决方案,但对我们来说效果很好。

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松弛解的讨论可以在这里找到:https://kotlinlang.slack.com/archives/CJLTWPH7S/p1649956718414129

xghobddn

xghobddn3#

使用SubComposeLayout可以实现这样一个特性来设置每个元素的高度,它允许您基于新的度量标准(如具有最大宽度或高度的兄弟元素)重新测量可组合元素。
你可以检查SubComposeLayout here的描述,或者我的答案,让列在这里等宽。

@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")
        }
    }
}

结果

mepcadol

mepcadol4#

下面的例子产生了你所寻找的行为。它利用了内在的大小。

@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()
        )
    }
}

kxxlusnw

kxxlusnw5#

所描述的行为是Compose中的一个错误,于2022年2月11日发布reported。该错误已于2022年8月13日标记为已修复。但尚不清楚哪个Compose版本将包含此修复。

**更新:**该错误已在compose-foundation版本1.3.0-beta 01中修复。

o4hqfura

o4hqfura6#

为了解决这个问题,我在Text()中使用了onTextLayout

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

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

iezvtpos

iezvtpos7#

有点晚了,但这里是解决方案。你几乎就到了。你想要的是行高等于所有子列项目的最大高度,所以你想要的是设置行高为“IntrinsicSize.Max”。诀窍是使用一个Spacer()组成以填充任何需要填充的空间,而不影响列本身的固有高度。如果您将间隔物的重量设置为“1f”,它将填充剩余的空间。

@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

相关问题