本文已参与「新人创作礼」活动,一起开启掘金创作之路。
Compose 支持约束布局,在使用约束布局之前需要先通过 gradle 导入:
implementation "androidx.constraintlayout:constraintlayout-compose:1.1.0-alpha02"
一、创建约束
当我们用 Android Studio 创建一个新项目时,Android Studio 会为我们默认生成一个约束布局,它包含一个 TextView,文字是 "Hello World!",位置居中。
如果用 Compose 实现同样的效果,代码如下:
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout(modifier = Modifier.fillMaxHeight().fillMaxWidth()) {
val (text) = createRefs()
Text("Hello World!", Modifier.constrainAs(text) {
top.linkTo(parent.top)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
})
}
}
首先,使用 val (text) = createRefs() 创建引用,然后在 Text() 函数的 Modifier 中,通过 Modifier.constrainAs(text) 将其与引用关联上。在只有一个控件时,暂时看不出建立引用有什么作用。所以建立引用的作用我们待会再讲。
建立引用后,通过 linkTo 函数,指定其上下左右四个方向的约束。这里和之前 xml 中定义约束的作用一样,只有写法上的区别。
运行效果如下:
二、控件之间的约束
在添加控件与控件之间的约束时,就能看出建立引用的作用了。比如我们建立两个 Text 之间的上下约束,代码如下:
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout(modifier = Modifier.fillMaxHeight().fillMaxWidth()) {
val (text1, text2) = createRefs()
Text("Hello World!", Modifier.constrainAs(text1) {
top.linkTo(parent.top)
bottom.linkTo(text2.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
})
Text("Hello World!", Modifier.constrainAs(text2) {
top.linkTo(text1.bottom)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
})
}
}
首先,通过 val (text1, text2) = createRefs() 建立两个引用,然后两个控件通过 Modifier.constrainAs(text1) 认领自己的引用。在添加约束条件时,就能取到每个引用的上下左右四个边了。
运行效果如下:
三、Barrier
在约束布局中,有个功能叫 Barrier,译为屏障。Compose 通过 createXXXBarrier 函数创建屏障:
val barrier1 = createTopBarrier(text1, text2)
val barrier2 = createBottomBarrier(text1, text2)
val barrier3 = createStartBarrier(text1, text2)
val barrier4 = createEndBarrier(text1, text2)
使用方式也很简单,添加 linkTo(barrier) 约束条件即可。
注:由于本文重点不是讲约束布局,只是讲 Compose 的约束布局和 xml 定义的约束布局的区别,所以不再细讲屏障的作用,下同。
四、Guideline
在约束布局中,Guideline 也很常用,Compose 通过 createGuidelineFromXXX 函数创建 Guideline:
val guideline1 = createGuidelineFromStart(0.5f)
val guideline2 = createGuidelineFromStart(100.dp)
val guideline3 = createGuidelineFromAbsoluteLeft(100.dp)
可以从上下左右四个方向创建 Guideline,可以通过百分比或固定长度来指定 Guideline 的位置。需要注意的是,通常我们指定左右时,都不是用 left/right,而是用 start/end。这是为了让应用便于被那些从右到左阅读的国家使用。这也是 createGuidelineFromAbsoluteLeft 和 createGuidelineFromStart 的区别。
使用方式同样是添加 linkTo(guideline) 约束条件即可。
五、解耦约束条件
前文说过,Compose 作为声明式 UI,不支持为控件设置 id,不能通过 id 获取到控件实例。
但在设置约束布局时,没有 id 实在是太不方便了,无法设置 id 导致约束无法被复用。所以使用约束布局时,可以给控件设置 id。
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout(
constraintSet(),
modifier = Modifier
.fillMaxHeight()
.fillMaxWidth()
) {
Text("Hello World!", Modifier.layoutId("text1"))
Text("Hello World!", Modifier.layoutId("text2"))
}
}
fun constraintSet() = ConstraintSet {
val text1 = createRefFor("text1")
val text2 = createRefFor("text2")
constrain(text1) {
top.linkTo(parent.top)
bottom.linkTo(text2.top)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
constrain(text2) {
top.linkTo(text1.bottom)
bottom.linkTo(parent.bottom)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
}
在这份代码中,通过 ConstraintSet() 函数创建一个约束集,通过 createRefFor 为控件 id 指定一个引用。然后在约束布局中,将这个约束集传入。再给每个控件指定一个 layoutId 就完成了约束条件的解耦。并且使得约束布局可以被复用。