在 Jetpack Compose 中将两个有序组件的第一个组件居中对齐的最简单方法是什么?

What is the simplest way to align center on first component of two ordered component in jetpack compose?

我想将红色表面与页面中心对齐,我该怎么做?

@Composable
fun Screen() {

    Row(
        modifier = Modifier.fillMaxSize(),
        horizontalArrangement = Arrangement.Center,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Surface(
            color = Color.Red,
            modifier = Modifier.size(100.dp, 50.dp)
        ){}
        Surface(
            color = Color.Blue,
            modifier = Modifier.size(100.dp, 50.dp)
        ) {}
    }
}

查看:

想要这个:

如果需要精确定位,您可以只使用 Layout

Layout(
 content = { Red(); Blue() } // Add Composables here
) { measurables, constraints ->
 layout(width = constraints.maxWidth, height = constraints.maxHeight) {
  measurables[0].measure(constraints).apply { //Red
   place(
    (constraints.maxWidth - width) / 2,
    (constraints.maxHeight - height) / 2  
   )
  }
  measurables[1].measure(constraints).apply {
   place(
    (constraints.maxWidth + width) / 2,
    (constraints.maxHeight - height) / 2
   )
  }
 }
}

这应该差不多了,你知道的,前提是红色和蓝色的宽度相同。如果不是,则必须将可测量对象的测量结果存储在变量中,而不是仅使用 apply。但这并不难——实际上非常简单,几乎没有什么不便。

解释:

我们调用一个简单的 Layout 可组合项并将我们想要的可组合项传递给它的 content 块,因为这些是我们必须定位的。然后,我们可以以 measurables 的形式访问这些相同的可组合项,我们可以使用它们来测量和放置我们想要的任何地方。我们还有一个 constraints 参数,在这种情况下就像屏幕的尺寸一样。我们 return 一个 layout 块,它本质上是所有所需可组合项的容器,我们指定宽度和高度,在本例中是这两个维度的最大值,对应于全屏这里。

measurables 是一个包含所有可组合项的数组,按顺序将它们传递到 content 块。因此,measurables[0]Red 块,蓝色在索引 1 上。现在,我们将红色块精确定位在屏幕的中心。尝试稍微调整 x 和 y 值以查看 UI 的反应。之后,我们将蓝色置于 x 等于 Redx + RedWidth(或蓝色,因为它们在这里相同)。我们这样做是因为我们希望蓝色恰好放在 Red 之后。 x 是从屏幕边缘到可组合项开始(或左边缘)的距离,而 y 是从其顶部开始的距离。

您可以使用 Layout 可组合项。
类似于:

Layout( content = {

    Surface(
        color = Color.Red,
        modifier = Modifier
            .size(100.dp, 50.dp)
            .layoutId("red")
    ) {}
    Surface(
        color = Color.Blue,
        modifier = Modifier
            .size(100.dp, 50.dp)
            .layoutId("blue")
    ) {}

}){ measurables, incomingConstraints ->

    val constraints = incomingConstraints.copy(minWidth = 0, minHeight = 0)
    val redPlaceable =
        measurables.find { it.layoutId == "red" }?.measure(constraints)
    val bluePlaceable =
        measurables.find { it.layoutId == "blue" }?.measure(constraints)

    //align red and blue
    layout(
        width = constraints.maxWidth, constraints.maxHeight)
        {
            val redPositionx = (constraints.maxWidth - widthOrZero(redPlaceable)) /2
            val redPositiony = (constraints.maxHeight - heightOrZero(redPlaceable)) /2
            redPlaceable?.placeRelative(redPositionx, redPositiony)
            bluePlaceable?.placeRelative(redPositionx+widthOrZero(redPlaceable), redPositiony)

    }
}

与:

internal fun widthOrZero(placeable: Placeable?) = placeable?.width ?: 0
internal fun heightOrZero(placeable: Placeable?) = placeable?.height ?: 0

如果您的蓝色视图具有静态尺寸,只需在另一侧添加相同尺寸的 Spacer

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically
) {
    val blueViewWidth = 100.dp
    Spacer(Modifier.width(blueViewWidth))
    RedView()
    BlueView(Modifier.size(blueViewWidth, 50.dp))
}

如果蓝色视图大小取决于内容,您可以使用它代替 Spacer 并应用 Modifier.alpha(0) - 这将比创建自定义布局性能低,但应该非常好,除非你的视图有一个非常大的布局来衡量。

Row(
    modifier = Modifier.fillMaxSize(),
    horizontalArrangement = Arrangement.Center,
    verticalAlignment = Alignment.CenterVertically
) {
    BlueView(Modifier.alpha(0f))
    RedView()
    BlueView()
}