一、ConstraintLayout
约束布局,使用前需要导入相应的依赖:
implementation "androidx.constraintlayout:constraintlayout-compose:1.0.1"
ConstraintLayou 最新版查询:https://developer.android.google.cn/jetpack/androidx/releases/constraintlayout?hl=zh-cn
1、创建与绑定引用
在传统的 View 视图开发中,约束布局通常在 XML 中为 View 组件设置 ID,然后使用 ID 作为索引来确定组件摆放的位置,而在 Compose 中可以创建引用并绑定至某个组件上,从而使用该引用来确定组件的摆放位置。
有两种方式可以创建引用,分别是 createRef()
和 createRefs()
,后者最多可以创建 16 个引用,创建方式如下所示:
val buttonRef = remember { createRef() }
val (textRef, imgRef) = remember { createRefs() }
只有在 ConstraintLayout 的作用域中才可以使用 createRef 和 createRefs 创建引用
Modifier.constrainAs()
用于将引用绑定到组件上,并可以在 constrainAs() 方法的 Lambda 中设置对齐位置。
示例代码如下所示:
ConstraintLayout(
modifier = Modifier
.size(300.dp)
.padding(10.dp)
.background(Color.LightGray)
) {
// 创建两个引用
val (helloRef, worldRef) = remember { createRefs() }
Text(
text = "Hello",
style = MaterialTheme.typography.h3,
// 绑定 helloRef 引用
modifier = Modifier.constrainAs(helloRef) {
// 这里 parent 指 Text 的父组件,即 ConstraintLayout
top.linkTo(parent.top)
end.linkTo(parent.end, 10.dp)
}
)
Text(
text = "World",
style = MaterialTheme.typography.h4,
// 绑定 worldRef 引用
modifier = Modifier.constrainAs(worldRef) {
top.linkTo(helloRef.bottom, 10.dp)
end.linkTo(helloRef.end)
}
)
}
Modifier.constrainsAs() 的 Lambda 是一个ConstrainScope 作用域,在其中可以获取到当前组件的 top、bottom、start、end 和父组件 parent 等信息,并使用 linkTo 指定约束。
除了在 ConstrainScope 中指定约束外,还可以在 ContrainScope 中设置组件的宽高等参数,如下所示:
ConstraintLayout(
modifier = Modifier
.size(300.dp)
.padding(10.dp)
.background(Color.LightGray)
) {
val surfaceRef = remember { createRef() }
Surface(
color = Color.Yellow,
modifier = Modifier.constrainAs(surfaceRef) {
width = Dimension.matchParent
height = Dimension.value(100.dp)
}
) {}
}
Dimension 可选值如下表所示:
Dimension 可选值 | 功能 |
---|---|
wrapContent() | 根据内容自适应调整尺寸 |
matchParent() | 铺满父组件 |
fillToConstraints() | 根据约束信息拉伸尺寸 |
preferredWrapContent() | 如剩余空间大于内容尺寸时,为自适应尺寸,否则为剩余空间尺寸 |
ratio(String) | 根据字符串计算实际尺寸比例,如 "1:2" |
percent(Float) | 根据浮点数计算实际尺寸比例 |
value(Dp) | 设置为固定尺寸 |
preferredValue(Dp) | 如剩余空间大于固定尺寸时,为固定尺寸,否则为剩余空间尺寸 |
2、Barrier 分界线
将用户名和密码的文本和对应的输入框组合时,可能会遇到如下情况:
即输入框的左侧没有对齐。
为了能使输入框的左侧对齐并且自适应调整宽度,就需要用到 Barrier 组件,仅需在文本结束处添加一条分界线,如下所示:
ConstraintLayout(
modifier = Modifier.fillMaxSize()
) {
val (usernameTextRef, passwordTextRef, usernameInputRef,
passwordInputRef, dividerRef) = remember { createRefs() }
// 使用 createEndBarrier 创建一条结尾分界线,分界线会处于文本较长的文本组件结尾处
val barrier = createEndBarrier(usernameTextRef, passwordTextRef)
Text(
text = "用户名",
modifier = Modifier.constrainAs(usernameTextRef) {
top.linkTo(parent.top, 30.dp)
start.linkTo(parent.start, 10.dp)
}
)
OutlinedTextField(
value = "",
onValueChange = {},
modifier = Modifier
.constrainAs(usernameInputRef) {
// 将用户名输入框 start 位置设为分界线右 10dp 处
start.linkTo(barrier, 10.dp)
top.linkTo(usernameTextRef.top)
bottom.linkTo(usernameTextRef.bottom)
}
)
Text(
text = "密码",
modifier = Modifier.constrainAs(passwordTextRef) {
top.linkTo(usernameInputRef.bottom, 30.dp)
start.linkTo(parent.start, 10.dp)
}
)
OutlinedTextField(
value = "",
onValueChange = {},
modifier = Modifier
.constrainAs(passwordInputRef) {
// 将密码输入框 start 位置设为分界线右 10dp 处
start.linkTo(barrier, 10.dp)
top.linkTo(passwordTextRef.top)
bottom.linkTo(passwordTextRef.bottom)
}
)
}
3、Guideline 引导线
Guideline 可以直接创建出一条引导线,下面是使用 Guideline 实现 Text 在屏幕水平垂直居中的例子:
ConstraintLayout(
modifier = Modifier.fillMaxSize()
) {
val textRef = remember { createRef() }
// 创建一个从顶部开始的引导线,使引导线位于屏幕垂直居中
val verticalGuideline = createGuidelineFromTop(.5f)
// 创建一个从左侧开始的引导线,使引导线位于屏幕水平居中
val horizontalGuideline = createGuidelineFromStart(.5f)
Text(
text = "垂直居中",
modifier = Modifier.constrainAs(textRef) {
// 将 Text 连接至引导线
top.linkTo(verticalGuideline)
bottom.linkTo(verticalGuideline)
start.linkTo(horizontalGuideline)
end.linkTo(horizontalGuideline)
}
)
}
4、Chain 链接约束
通过 Chain 链接约束可以使多个组件平均分配布局空间,功能类似于 weight。
Chain 提供了三种样式可供选择,如下所示:
Chain 布局样式 | 功能 |
---|---|
ChainStyle.Spread | 每个元素平分整个父空间 |
ChainStyle.SpreadInside | 首位元素紧贴边界,剩余元素平分父空间 |
ChainStyle.Packed | 所有元素聚集到中间 |
下面展示使用 Chain 将四个按钮根据 Chain 提供的不同的布局样式进行排布。
ConstraintLayout(
modifier = Modifier.fillMaxSize()
) {
val (btn1Ref, btn2Ref, btn3Ref, btn4Ref) = remember { createRefs() }
// 创建一条垂直的约束链接将四个按钮连接起来,并指定链接样式
createVerticalChain(btn1Ref, btn2Ref, btn3Ref,btn4Ref, chainStyle = ChainStyle.Packed)
Button(
onClick = {},
// 绑定样式,下同
modifier = Modifier.constrainAs(btn1Ref) {}
) {
Text("Btn1")
}
Button(
onClick = {},
modifier = Modifier.constrainAs(btn2Ref) {}
) {
Text("Btn1")
}
Button(onClick = {},
modifier = Modifier.constrainAs(btn3Ref) {}
) {
Text("Btn1")
}
Button(
onClick = {},
modifier = Modifier.constrainAs(btn4Ref) {}
) {
Text("Btn1")
}
}
Packed:
SpreadInside:
Spread: