Jetpack Compose 中的 ConstraintLayout

1,027 阅读2分钟

[ConstraintLayout] 是一种布局,让您可以相对于屏幕上的其他可组合项来放置可组合项。它是一种实用的替代方案,可代替使用多个已嵌套的 RowColumnBox 和[其他自定义布局元素]这种做法。在实现对齐要求比较复杂的较大布局时,ConstraintLayout 很有用。

在以下情况下,考虑使用 ConstraintLayout

  • 为了避免在屏幕上定位元素时嵌套多个 Column 和 Row,以便提高代码的可读性。
  • 相对于其它可组合项来定位可组合项,或根据引导线、屏障线或链来定位可组合项。

在 View 系统中,建议使用 ConstraintLayout 来创建复杂的大型布局,因为扁平视图层次结构比嵌套视图的效果更好。不过,这在 Compose 中不是什么问题,因为 Compose 能够高效地处理较深的布局层次结构。

开始使用 ConstraintLayout

如需使用 Compose 中的 ConstraintLayout,您需要在 build.gradle 中添加以下依赖项(除了 Compose 设置外):

implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"

Compose 中的 ConstraintLayout 使用 [DSL] 按以下方式运作:

  • 使用 [createRefs()] 或 [createRefFor()] 为 ConstraintLayout 中的每个可组合项创建引用。
  • 约束条件是使用 constrainAs() 修饰符提供的,该修饰符将引用作为参数,可让您在主体 lambda 中指定其约束条件。
  • 约束条件是使用 linkTo() 或其他有用的方法指定的。
  • parent 是一个现有的引用,可用于指定对 ConstraintLayout 可组合项本身的约束条件。

下面是使用 ConstraintLayout 的可组合项的示例:

image.png

ConstraintLayout(modifier = Modifier.background(Color.LightGray)) {
    val (button, text) = createRefs()
    Button(
        onClick = { /* Do something */ },
        modifier = Modifier.constrainAs(button) {
            top.linkTo(parent.top, margin = 16.dp)
        }
    ) {
        Text("Button")
    }
    Text("Text",
        Modifier.constrainAs(text) {
            top.linkTo(button.bottom, margin = 16.dp)
        }
    )
}

Decoupled API

在 ConstraintLayout 示例中,约束条件是在应用它们的可组合项中使用修饰符以内嵌方式指定的。不过,在某些情况下,最好将约束条件与应用它们的布局分离开来。例如,您可能会希望根据屏幕配置来更改约束条件,或在两个约束条件集之间添加动画效果。

对于此类情况,您可以通过不同的方式使用 ConstraintLayout

  1. 将 [ConstraintSet] 作为参数传递给 ConstraintLayout
  2. 使用 [layoutId] 修饰符将在 ConstraintSet 中创建的引用分配给可组合项。
  • 竖屏状态下

image.png

  • 横屏状态下 (横屏下 top 间距加大)

image.png

@Composable
fun DecoupledConstraintLayout() {
    val configuration = LocalConfiguration.current
    val constraints = if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
        decoupledConstraints(margin = 0.dp) // 竖屏约束
    } else {
        decoupledConstraints(margin = 32.dp) // 横屏约束
    }
    Box(modifier = Modifier.padding(all = 5.dp)) {
        ConstraintLayout(constraintSet = constraints, modifier = Modifier.background(Color.LightGray)) {
            Button(
                onClick = { },
                modifier = Modifier.layoutId("button")
            ) {
                Text("Button")
            }
            Text("Text", Modifier.layoutId("text"))
        }
    }
}

private fun decoupledConstraints(margin: Dp): ConstraintSet {
    return ConstraintSet {
        val button = createRefFor("button")
        val text = createRefFor("text")

        constrain(button) {
            top.linkTo(parent.top, margin = margin)
        }
        constrain(text) {
            top.linkTo(button.bottom, margin)
        }
    }
}

上一篇 Jetpack Compose FlowRow

下一篇 Jetpack Compose 抽屉式导航栏