Compose中的布局

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

简单示例:

@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

效果如下:

Column

然后我们让内容显示在中间,添加如下代码:

verticalArrangement = Arrangement.Center, // 指定垂直方向居中显示
horizontalAlignment = Alignment.CenterHorizontally // 指定水平方向居中对齐
1
2

效果如下:

Column内容居中

# 水平线性布局

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

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

效果如下:

Row

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

其中SpaceBetween、SpaceAround、SpaceEvenly分别如下:

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

简单示例:

@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

效果如下:

Box

现在我们添加如下代码:

propagateMinConstraints = true
1

发现Text填满了Box:

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

效果如下,完全符合预期:

约束布局

然后我们来看下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

我们通过createGuidelineFromBottom()创建了一个从底部开始的GuideLine,参数可以是dp,也可以是浮点数,浮点数就是百分比,效果如下:

GuideLine

然后我们看下Barrier:

// 创建Barrier,参数为控件对应的id,尾部barrier位于两个控件的尾部
val barrier = createEndBarrier(btn_click, tv_bottom)
1
2

很简单,跟GuideLine没啥区别,参数就是想要编组的控件的id。用法就不再说了,跟GuideLine一样使用就行。

Tips:createGuideLineFromXXX/createXXXBarrier有很多基于方向的api,直接调用即可,而且借助GuideLine也能实现百分比布局。

# 2 Chain

Chain是ConstraintLayout的重点之一,它有两个api:

// 横向的Chain
createHorizontalChain()
// 竖向的Chain
createVerticalChain()
1
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

效果如下:

Packed

Spread的效果:

Spread

SpreadInside的效果:

SpreadInside

Last Updated: 1/28/2022, 4:13:43 PM