Compose 中的 ConstraintLayout
ConstraintLayout 是一种布局,让您可以相对于屏幕上的其他可组合项来放置可组合项。它是使用多个嵌套的 Row、Column、Box 等布局的实用替代方案,特别适合实现对齐要求复杂的大型布局。
何时使用 ConstraintLayout
- 为了避免嵌套多个
Column和Row,提高代码可读性 - 需要相对于其他可组合项定位元素
- 需要根据引导线、屏障线或链来定位可组合项
注意:在 Compose 中,深度嵌套的布局层次结构效率也很高,所以使用 ConstraintLayout 主要是为了代码可读性和复杂布局需求,而非性能优化。
开始使用
首先在 build.gradle 中添加依赖项:
implementation "androidx.constraintlayout:constraintlayout-compose:$constraintlayout_compose_version"
注意:constraintLayout-compose 工件的版本控制不同于 Jetpack Compose。查看 ConstraintLayout 版本页面获取最新版本。
基本用法
Compose 中的 ConstraintLayout 使用 DSL 操作:
- 使用
createRefs()为每个可组合项创建引用 - 通过
constrainAs()修饰符应用约束 - 使用
linkTo()方法指定约束关系 - 使用
parent引用来约束到父容器
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout {
// 创建可组合项的引用
val (button, text) = createRefs()
Button(
onClick = { /* Do something */ },
// 将引用"button"分配给Button可组合项
// 并将其约束到ConstraintLayout的顶部
modifier = Modifier.constrainAs(button) {
top.linkTo(parent.top, margin = 16.dp)
}
) {
Text("Button")
}
// 将引用"text"分配给Text可组合项
// 并将其约束到Button底部
Text(
"Text",
Modifier.constrainAs(text) {
top.linkTo(button.bottom, margin = 16.dp)
}
)
}
}
Decoupled API
有时需要将约束条件与布局分离,例如根据屏幕配置更改约束或在约束集之间添加动画:
@Composable
fun DecoupledConstraintLayout() {
BoxWithConstraints {
val constraints = if (minWidth < 600.dp) {
decoupledConstraints(margin = 16.dp) // 纵向约束
} else {
decoupledConstraints(margin = 32.dp) // 横向约束
}
ConstraintLayout(constraints) {
Button(
onClick = { /* Do something */ },
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)
}
}
}
ConstraintLayout 概念
引导线
引导线是用于定位元素的视觉辅助工具,有两种类型:垂直和水平。
ConstraintLayout {
// 在父容器宽度10%处创建从起始位置开始的引导线
val startGuideline = createGuidelineFromStart(0.1f)
// 在父容器宽度10%处创建从结束位置开始的引导线
val endGuideline = createGuidelineFromEnd(0.1f)
// 在距离父容器顶部16dp处创建引导线
val topGuideline = createGuidelineFromTop(16.dp)
// 在距离父容器底部16dp处创建引导线
val bottomGuideline = createGuidelineFromBottom(16.dp)
}
屏障线
屏障线引用多个可组合项,根据指定边中最边缘的 widget 创建虚拟引导线。
ConstraintLayout {
val constraintSet = ConstraintSet {
val button = createRefFor("button")
val text = createRefFor("text")
val topBarrier = createTopBarrier(button, text)
}
}
链
链在单条轴(水平或垂直)上提供类似组的行为。
ConstraintLayout {
val constraintSet = ConstraintSet {
val button = createRefFor("button")
val text = createRefFor("text")
val verticalChain = createVerticalChain(button, text, chainStyle = ChainStyle.Spread)
val horizontalChain = createHorizontalChain(button, text)
}
}
链可通过不同的 ChainStyle 配置:
ChainStyle.Spread:空间均匀分布在所有可组合项之间,包括首尾元素外的空间ChainStyle.SpreadInside:空间均匀分布在所有可组合项之间,不包括首尾元素外的空间ChainStyle.Packed:空间分布在首尾元素之外,各元素挤在一起
如需更多示例,可查看 Compose 示例 中的实际应用。