概述
Compose 中的线性布局对应的是Android传统视图中的LinearLayout,不一样的地方是,Compose根据Orientation的不同又将布局分为Column和Row, Column对应传统视图LinearLayout中orientation = “vertical”的情况,Row对应传统视图LinearLayout中orientation = “horizontal”的情况.由于两者内部元素在父容器中的布局和对其方式不同,分成两个组件有助于提供类型安全的Modifier修饰符。而Compose中的帧布局对应的是Android传统View中的FrameLayout,它可以让自己的子组件依次按照顺序推叠。
实例解析
1.线性布局
在Compose中,线性布局根据使用场景的不同,分为Column和Row,Column 是垂直线性布局组件,Row是水平线性布局组件
1.1 Column组件
@Composable
inline fun Column(
modifier: Modifier = Modifier,
verticalArrangement: Arrangement.Vertical = Arrangement.Top,
horizontalAlignment: Alignment.Horizontal = Alignment.Start,
content: @Composable ColumnScope.() -> Unit
)
上面是Column的参数列表,其中verticalArrangement和horizontalAlignment参数分别可以帮助我们安排子项的垂直/水平位置,在默认情况下,子项会以垂直方向靠上(Arrangment.Top)、水平方向靠左来布置子项的摆放
我们可以以一个例子看下Column的使用
@Composable
fun LinearLayoutDemo() {
Column(
modifier = Modifier
.border(1.dp, color = Color.Red)
.size(150.dp),
verticalArrangement = Arrangement.Center
) {
Text(
text = "Hello World",
style = MaterialTheme.typography.h6,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Text(
text = "JetPack",
style = MaterialTheme.typography.h6,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
Text(
text = "zhongxj",
style = MaterialTheme.typography.h6,
modifier = Modifier.align(Alignment.CenterHorizontally)
)
}
}
运行结果:
在上面的代码中,我们通过verticalArrangement参数,将Column中的Text摆放到了组件的中间,然后使用Modifier.align修饰符来独立设置子项的对齐规则
这里可能有人会问,不是有horizontalAlignment参数吗,水平方向为啥还要用Modifier.align呢?因为对于垂直布局中的子项,Modifier.align只能设置自己在水平方向的位置,反之水平布局的子项,只能设置自己在垂直方向的位置,就比如我们正在介绍的Column组件,当Column组件中有多个子项时,他们在垂直方向永远是线性排列的,如果允许子项被单独设置,就会出现不好的情况,比如Column中有A,B,C三项,如果配置A的对齐方向是Aligment.Bottom,B为Aligment.Top,这显然是无法实现的,所以Column的子项在垂直方向布局只能通过verticalArragnment进行整体设置。(注意:这里不是说Column只能使用verticalArragnment参数)
注意:在不给Column指定高度、宽度、大小的情况下,Column组件会默认包裹里面的子项,在这个时候我们是无法使用Column参数中的verticalArrangement或者horizontalAlignment来定位子项在Column中的整体位置的
1.2 Row组件
Row组件可以将内部子项按照从左到右的方向水平排列,和Cloumn组件配合使用就可以构建出很丰富美观的界面。下面是一个使用Row组件和Column组件制作的文章卡片,代码如下:
@Composable
fun ArticleCard() {
Surface(
shape = RoundedCornerShape(8.dp),
modifier = Modifier
.padding(horizontal = 12.dp)
.fillMaxWidth(),
elevation = 10.dp,
) {
Surface(modifier = Modifier.padding(12.dp), color = Color(0xeeeeeeee)) {
Column(modifier = Modifier.padding(12.dp)) {
Text(
text = "JetPack Compose",
style = MaterialTheme.typography.h6
)
Spacer(modifier = Modifier.padding(vertical = 5.dp))
Text(
text = " Jetpack Compose是第一个使用Kotlin正在开发中的大型项目," +
"因此Android团队正在探索Kotlin API指南的新世界,以创建一组特定于Compose API的指南," +
"该工作仍在进行中,仍然有很长的路要"
)
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween
) {
IconButton(onClick = { /*TODO*/ }) {
Icon(
imageVector = Icons.Filled.Favorite,
contentDescription = null, modifier = Modifier.size(16.dp)
)
}
IconButton(onClick = { /*TODO*/ }) {
Icon(
painterResource(id = R.drawable.comment),
contentDescription = null, modifier = Modifier.size(16.dp)
)
}
IconButton(onClick = { /*TODO*/ }) {
Icon(
painterResource(id = R.drawable.share),
contentDescription = null, modifier = Modifier.size(16.dp)
)
}
}
}
}
}
}
运行结果:
从上面的代码中可知,Row的horizontalArrangement参数帮助我们合理配置了按钮的水平位置。可以看到,图标部分,喜欢和分享按钮呈左右两端对齐,Arrangement定义了很多子项对齐的方式,除了Center(居中),Start(水平靠左),End(水平靠右)等常见的对齐方式,还有一些特定场景下的对齐方式,如Space Between,Space Evenly等。
2.帧布局
2.1 Box组件
Box组件是一个能够将里面的子项依次按照顺序堆叠的布局组件,在使用上类似于传统布局的FrameLayout,这个很简单,我们看下一段代码了解下它的使用。
@Composable
fun BoxDemo(){
Box(modifier = Modifier
.size(150.dp)
.background(Color.Green))
Box(modifier = Modifier
.size(80.dp)
.background(Color.Red))
Text(text = "Hello World")
}
运行结果:
2.2 Surface组件
Surface从字面上理解是一个平面,在Material Design设计准则中也同样如此,我们可以将很多的组件摆放在这个平台之上,可以设置这个平面的边框,圆角,颜色等.例如,使用Surface实现一个卡片效果,代码如下:
@Composable
fun SurfaceDemo() {
Surface(
shape = RoundedCornerShape(8.dp),
elevation = 10.dp,
modifier = Modifier
.width(300.dp)
.height(100.dp),
color = Color.Gray
) {
Row(modifier = Modifier.clickable {}) {
Image(
painter = painterResource(id = R.drawable.portrait),
contentDescription = null,
modifier = Modifier.size(100.dp),
contentScale = ContentScale.Crop
)
Spacer(modifier = Modifier.padding(horizontal = 12.dp))
Column(
modifier = Modifier.fillMaxHeight(),
verticalArrangement = Arrangement.Center
) {
Text(text = "zhongxj", style = MaterialTheme.typography.h6)
Spacer(modifier = Modifier.padding(vertical = 8.dp))
Text(text = "海塔灯")
}
}
}
}
运行结果:
从上面的代码中我们可以看到,在Surface中我们主要编写UI代码,而Surface主要负责整个组件的形状,阴影,背景等,Surface可以帮助我们解耦一些代码,而不必在单个组件上添加很多的Modifier修饰符方法。
很多读者可能会有疑问有了Box为啥还要加一个Surface组件,其实Box和Surface组件还是有区别的,如果我们需要快速设置界面的形状,阴影,边框,颜色等,我们使用Surface会更好,因为这样可以减少Modifier的使用量。而如果我们只是需要简单设置界面背景颜色,大小,且需要简单布局下子项的位置,则可以使用Box
3.Spacer留白
在很多时候,我们需要让组件之间留有空白的间隙,这时候就可以使用Compose提供的Spacer组件,简单用一个demo展示Spacer的使用。
@Composable
fun SpacerDemo(){
Row {
Box(modifier = Modifier
.size(100.dp)
.background(Color.Green))
Spacer(modifier = Modifier.width(20.dp))
Box(modifier = Modifier
.size(100.dp)
.background(Color.Yellow))
Spacer(modifier = Modifier.weight(1f))
Box(modifier = Modifier
.size(100.dp)
.background(Color.Magenta))
}
}
运行结果
从上面的代码中可以看出Spacer的另一种使用场景,那就是在代码中使用Box绘制占位的矩形块在没有内容的时候完成可以使用Spacer来替代。
总结
本文主要讲了线性布局和帧布局在Compose中如何使用,其实非常简单,就需要我们多加练习,没有啥技巧。等啥时候我们写Compose的UI和写XML的UI一样熟练的时候,我们的工作效率就会大大的提升,可以用更少的代码实现更炫的UI效果,而且Kotlin和Java还可以混合使用,现在我已经在用Compose写测试的界面了,非常方便,推荐读者也动起来,使用Java的小伙伴可以使用Compose写界面,Java写功能,而使用Kotlin的小伙伴,则可以更方便的使用Compose,一种语言,完成界面和功能,真的特别酷。