17、Compose布局:Column、Row、Box、ConstraintLayout、LazyColumn、实战:复杂布局实现。
布局,是UI的骨架。在Jetpack Compose里,布局的玩法跟传统View体系完全不同。你不再写XML,而是用Kotlin代码直接描述UI。刚开始我也有点不习惯,但用顺手之后,真香。
今天这一章,咱们就把Compose里最核心的几个布局组件掰开揉碎。Column、Row、Box、ConstraintLayout、LazyColumn,再加上一个复杂布局的实战。嗯,内容不少,但都是干货。
17.1 Column:纵向排列,最常用的布局
Column就是垂直方向的线性布局。说白了,它把子组件从上到下依次排开。我在项目中用它来搭建列表页、表单页、详情页,几乎每天都在用。
@Composable
fun ColumnDemo() {
Column(
modifier = Modifier
.fillMaxSize()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text("第一行")
Text("第二行")
Text("第三行")
}
}
17.2 Row:横向排列,跟Column是对称的
Row就是水平方向的线性布局。它把子组件从左到右依次排开。你想想看,导航栏、标签栏、操作按钮组,这些场景都离不开Row。
@Composable
fun RowDemo() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(8.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically
) {
Icon(Icons.Default.Home, contentDescription = null)
Icon(Icons.Default.Favorite, contentDescription = null)
Icon(Icons.Default.Settings, contentDescription = null)
}
}
17.3 Box:层叠布局,像FrameLayout
Box允许子组件在Z轴上层叠。说白了,就是可以一个组件叠在另一个组件上面。我经常用它来实现角标、遮罩层、水印效果。
@Composable
fun BoxDemo() {
Box(
modifier = Modifier.size(200.dp),
contentAlignment = Alignment.Center
) {
Image(
painter = painterResource(id = R.drawable.bg),
contentDescription = null,
modifier = Modifier.fillMaxSize()
)
Text(
text = "Hello",
color = Color.White,
fontSize = 24.sp
)
}
}
17.4 ConstraintLayout:复杂布局的利器
当布局层级超过3层,或者组件之间有多重依赖关系时,Column和Row就不够用了。这时候我会掏出ConstraintLayout。它跟View体系里的ConstraintLayout思路一致,但写法更简洁。
@Composable
fun ConstraintLayoutDemo() {
ConstraintLayout(
modifier = Modifier.fillMaxSize()
) {
val (button, text, image) = createRefs()
Button(
onClick = { },
modifier = Modifier.constrainAs(button) {
top.linkTo(parent.top, margin = 16.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
) {
Text("点击")
}
Text(
text = "描述文字",
modifier = Modifier.constrainAs(text) {
top.linkTo(button.bottom, margin = 8.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
Image(
painter = painterResource(id = R.drawable.ic_launcher),
contentDescription = null,
modifier = Modifier.constrainAs(image) {
top.linkTo(text.bottom, margin = 16.dp)
start.linkTo(parent.start)
end.linkTo(parent.end)
}
)
}
}
17.5 LazyColumn:高性能列表
列表是移动端最常用的UI形态。LazyColumn就是Compose里的RecyclerView。它只渲染可见项,性能极好。我在项目里用它来展示聊天记录、商品列表、新闻流。
@Composable
fun LazyColumnDemo() {
val items = (1..100).toList()
LazyColumn(
modifier = Modifier.fillMaxSize(),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(items) { item ->
Card(
modifier = Modifier.fillMaxWidth(),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) {
Text(
text = "第 $item 项",
modifier = Modifier.padding(16.dp)
)
}
}
}
}
17.6 实战:复杂布局实现
光说不练假把式。咱们来做一个真实场景:一个商品详情页。它包含顶部图片、标题、价格、规格选择、评价列表。这个布局用单一组件搞不定,得组合使用。
@Composable
fun ProductDetailScreen() {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState())
) {
// 顶部图片
Image(
painter = painterResource(id = R.drawable.product),
contentDescription = null,
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
)
// 标题和价格
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "高端机械键盘",
fontSize = 24.sp,
fontWeight = FontWeight.Bold
)
Text(
text = "¥299.00",
color = Color.Red,
fontSize = 20.sp,
modifier = Modifier.padding(top = 8.dp)
)
}
// 规格选择
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
Chip(text = "黑色")
Chip(text = "白色")
Chip(text = "蓝色")
}
// 评价列表
LazyColumn(
modifier = Modifier
.fillMaxWidth()
.height(200.dp),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
items(5) { index ->
Text("用户评价 $index:手感很好,推荐购买!")
}
}
}
}
@Composable
fun Chip(text: String) {
Surface(
shape = RoundedCornerShape(16.dp),
color = Color.LightGray,
modifier = Modifier.padding(4.dp)
) {
Text(
text = text,
modifier = Modifier.padding(horizontal = 12.dp, vertical = 6.dp)
)
}
}
17.7 知识体系总览
为了让你更直观地理解这些布局组件的关系,我画了一张图。它展示了每个组件的核心用途和适用场景。
17.8 总结与建议
Compose的布局体系,说白了就是几个核心组件的灵活组合。Column和Row解决线性排列,Box解决层叠,ConstraintLayout解决复杂约束,LazyColumn解决长列表。你想想看,大部分UI场景都能用这五个组件搞定。
我在项目里总结了一个原则:能用Column/Row解决的,绝不用ConstraintLayout。因为Column/Row的代码更简洁,性能也更好。只有当布局层级超过3层,或者组件之间有交叉依赖时,才考虑ConstraintLayout。