Compose中的布局
LloydFinch 8/18/2021 AndroidCompose布局
# 线性布局Column和Row
# 竖直线性布局
API如下:
inline fun Column(
modifier: Modifier = Modifier, // 修饰符
verticalArrangement: Arrangement.Vertical = Arrangement.Top, // 类似于gravity,指定内容在垂直方向的位置
horizontalAlignment: Alignment.Horizontal = Alignment.Start, // 内容对齐方式,指定内容在水平方向的位置
content: @Composable ColumnScope.() -> Unit // 内容
)
1
2
3
4
5
6
2
3
4
5
6
简单示例:
@Composable
fun ColumnDemo() {
Column(
modifier = Modifier
.size(width = 200.dp, height = 400.dp)
.background(Color.Red),
) {
Text(text = "text1")
Text(text = "text2")
Text(text = "text3")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
效果如下:
然后我们让内容显示在中间,添加如下代码:
verticalArrangement = Arrangement.Center, // 指定垂直方向居中显示
horizontalAlignment = Alignment.CenterHorizontally // 指定水平方向居中对齐
1
2
2
效果如下:
# 水平线性布局
API如下:
inline fun Row(
modifier: Modifier = Modifier, 修饰符
horizontalArrangement: Arrangement.Horizontal = Arrangement.Start, // 类似于gravity,指定内容在水平方向的位置
verticalAlignment: Alignment.Vertical = Alignment.Top, // 内容对齐方式,只有垂直方向的,指定内容在垂直方向的位置
content: @Composable RowScope.() -> Unit // 内容
)
1
2
3
4
5
6
2
3
4
5
6
Row等价于横向的线性布局,跟Column用法类似,如下:
@Composable
fun RowDemo() {
Row(
modifier = Modifier
.size(width = 200.dp, height = 400.dp)
.background(Color.Red),
verticalAlignment = Alignment.CenterVertically, // 垂直居中
horizontalArrangement = Arrangement.SpaceBetween // 水平方向: 前后没有空隙,且子view之间均匀分散
) {
// 添加背景来区分
Text(text = "text1",modifier = Modifier.background(Color.Green))
Text(text = "text1",modifier = Modifier.background(Color.Green))
Text(text = "text1",modifier = Modifier.background(Color.Green))
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
效果如下:
Arrangement有如下枚举值:
Arrangement.Start // 子view排列在头部
Arrangement.End // 子view排列在尾部
Arrangement.Center // 子view排列在中间
Arrangement.SpaceBetween // 首尾没有空隙,且子view之间均匀分散
Arrangement.SpaceAround // 首尾空隙是每个子view之间空隙的一半
Arrangement.SpaceEvenly // 让每个子view之间的空隙均匀分散,包括首尾
1
2
3
4
5
6
2
3
4
5
6
其中SpaceBetween、SpaceAround、SpaceEvenly分别如下:
# 盒布局Box(帧布局)
API如下
inline fun Box(
modifier: Modifier = Modifier, // 修饰符
contentAlignment: Alignment = Alignment.TopStart, // 内容对齐方式(水平和垂直),默认是左上角(LTR情况下)
propagateMinConstraints: Boolean = false, // 是否将最小约束传给内部View,如果设置为true,则内容会填满Box本身
content: @Composable BoxScope.() -> Unit // 内部布局
)
1
2
3
4
5
6
2
3
4
5
6
简单示例:
@Composable
fun BoxDemo() {
Box(
modifier = Modifier
.size(width = 200.dp, height = 200.dp)
.background(Color.Red),
contentAlignment = Alignment.Center, // 居中对齐
) {
Text(
text = "BoxLayout", modifier = Modifier
.size(width = 100.dp, height = 100.dp)
.background(Color.Green)
)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
效果如下:
现在我们添加如下代码:
propagateMinConstraints = true
1
发现Text填满了Box:
# 约束布局ConstraintLayout
@Composable
fun ConstraintLayoutDemo(context: Context) {
ConstraintLayout(
modifier = Modifier
.background(Color.Gray) // 添加灰色背景来区分
.size(width = 300.dp, height = 500.dp) // 设置背景
) {
// 创建两个id
val (btn_click, text_hello) = createRefs()
// 添加一个Button
Button(
onClick = { Toast.makeText(context, "Click Button", Toast.LENGTH_SHORT).show() },
modifier = Modifier.constrainAs(btn_click) { // 指定id为btn_click
top.linkTo(parent.top, margin = 32.dp) // 顶部和父布局顶部对齐,同时设置margin为32dp
start.linkTo(parent.start) // 左边和父布局左边对齐
end.linkTo(parent.end) // 右边和父布局右边对齐
bottom.linkTo(text_hello.top) // 底部和text_hello顶部对齐
}
) {
Text(text = "Button")
}
// 添加一个Text
Text(text = "Text",
modifier = Modifier
.background(Color.Red) //添加红色背景,方便区分
.constrainAs(text_hello) { // 指定id为text_hello
top.linkTo(
btn_click.bottom,
margin = 48.dp
) // 顶部和btn_click底部对齐,同时设置margin为48dp
start.linkTo(btn_click.start)
end.linkTo(btn_click.end)
bottom.linkTo(parent.bottom, margin = 32.dp) // 底部和parent底部对齐,同时设置margin为32dp
})
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
效果如下,完全符合预期:
然后我们来看下ConstraintLayout其他的 边角API
# 1 GuideLine和Barrier
我们添加如下代码:
// 创建一个guideline,位于底部50%的位置
val line_bottom_50F = createGuidelineFromBottom(fraction = 0.5F)
val (tv_bottom) = createRefs()
// Text底部跟guideline对齐
Text(
text = "Bottom_50",
modifier = Modifier
.background(Color.Yellow)
.constrainAs(tv_bottom) {
// 底部跟guideline对齐
bottom.linkTo(line_bottom_50F)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
我们通过createGuidelineFromBottom()创建了一个从底部开始的GuideLine,参数可以是dp,也可以是浮点数,浮点数就是百分比,效果如下:
然后我们看下Barrier:
// 创建Barrier,参数为控件对应的id,尾部barrier位于两个控件的尾部
val barrier = createEndBarrier(btn_click, tv_bottom)
1
2
2
很简单,跟GuideLine没啥区别,参数就是想要编组的控件的id。用法就不再说了,跟GuideLine一样使用就行。
Tips:createGuideLineFromXXX/createXXXBarrier有很多基于方向的api,直接调用即可,而且借助GuideLine也能实现百分比布局。
# 2 Chain
Chain是ConstraintLayout的重点之一,它有两个api:
// 横向的Chain
createHorizontalChain()
// 竖向的Chain
createVerticalChain()
1
2
3
4
2
3
4
他们的第一个参数都是控件的id,第二个参数就是ChainStyle,分别是:
- Packed: 所有控件挨在一起,居中
- Spread: 所有控件均匀分布在父布局中,这是ChainStyle的默认值
- SpreadInside: 第一个和最后一个控件分布在两端,其余平均分布在父布局中。
我们来看下Packed对应的效果:
// 创建引用(id)
var (tv_1, tv_2, tv_3) = createRefs()
// 创建Chain
val chain = createHorizontalChain(
elements = arrayOf(tv_1, tv_2, tv_3), // 关联的id
chainStyle = ChainStyle.Packed // 指定Chain类型
)
// 创建Chain关联的三个Text
Text(text = "tv1",modifier = Modifier.size(50.dp).background(Color.Red).constrainAs(tv_1){})
Text(text = "tv1",modifier = Modifier.size(50.dp).background(Color.Green).constrainAs(tv_2){})
Text(text = "tv1",modifier = Modifier.size(50.dp).background(Color.Yellow).constrainAs(tv_3){})
1
2
3
4
5
6
7
8
9
10
11
12
2
3
4
5
6
7
8
9
10
11
12
效果如下:
Spread的效果:
SpreadInside的效果: