约束布局 (ConstraintLayout)
虽然 Column、Row 和 Box 足以应付大多数场景,但在处理复杂的相对定位关系时,嵌套层级可能会变得很深。ConstraintLayout 可以让你构建扁平化的复杂布局。
添加依赖
Compose 的 ConstraintLayout 是一个独立的库:
kotlin
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"基本用法
- 使用
createRefs()创建引用。 - 使用
Modifier.constrainAs(ref)将组件绑定到引用。 - 在
constrainAslambda 中定义约束条件(linkTo)。
kotlin
@Composable
fun ConstraintLayoutContent() {
ConstraintLayout(modifier = Modifier.fillMaxSize()) {
// 1. 创建引用
val (button, text) = createRefs()
Button(
onClick = { /* Do something */ },
// 2. 绑定引用并添加约束
modifier = Modifier.constrainAs(button) {
// 顶部距离父容器顶部 16dp
top.linkTo(parent.top, margin = 16.dp)
// 居中
start.linkTo(parent.start)
end.linkTo(parent.end)
}
) {
Text("Button")
}
Text(
text = "Text",
modifier = Modifier.constrainAs(text) {
// 顶部位于 button 的底部,间距 16dp
top.linkTo(button.bottom, margin = 16.dp)
// 水平居中于 button
centerHorizontallyTo(button)
}
)
}
}屏障 (Barrier)
参考线会根据多个元素中最宽/最高的那个来定位。
kotlin
val barrier = createEndBarrier(button1, button2)
Text(
modifier = Modifier.constrainAs(text) {
start.linkTo(barrier)
}
)引导线 (Guideline)
不可见的参考线。
kotlin
val guideline = createGuidelineFromTop(0.5f) // 位于顶部 50% 处链 (Chain)
将多个组件按特定方式排列(类似 Row/Column 的 Arrangement)。
kotlin
createHorizontalChain(button1, button2, chainStyle = ChainStyle.Spread)解耦约束 (Decoupled API)
可以将约束逻辑与布局声明分离,这样可以根据屏幕配置(如横屏/竖屏)动态改变约束集 ConstraintSet,而无需重写 UI 结构。
kotlin
@Composable
fun DecoupledConstraintLayout() {
BoxWithConstraints {
val constraints = if (maxWidth < maxHeight) {
decoupledConstraints(margin = 16.dp) // 竖屏约束
} else {
decoupledConstraints(margin = 32.dp) // 横屏约束
}
ConstraintLayout(constraints) {
Button(
onClick = { /* Do something */ },
modifier = Modifier.layoutId("button") // 使用 ID 关联
) {
Text("Button")
}
Text(
text = "Text",
modifier = 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 = margin)
}
}
}