一、核心基础(必掌握)
Box 是 Compose 中最基础、最灵活的布局组件之一,核心作用是堆叠 / 对齐子组件(类似传统 View 体系的 FrameLayout),既支持单个组件的对齐,也支持多组件的层叠排列。
1. 核心特性
| 特性 | 说明 | 适用场景 |
|---|---|---|
| 层叠排列 | 子组件按声明顺序堆叠(后声明的在上方) | 带遮罩的图片、按钮 + 角标、文字叠加 |
| 对齐控制 | 支持子组件在 Box 内任意方向对齐 | 单个组件居中、右下角按钮等 |
| 尺寸自适应 | 默认包裹内容,也可设置固定 / 填充父布局 | 适配不同内容大小的容器 |
| 修饰符增强 | 支持背景、阴影、点击等修饰符 | 卡片、可点击容器 |
2. 最简示例
// 基础用法:单个组件居中
@Composable
fun BasicBoxDemo() {
Box(
modifier = Modifier
.size(200.dp) // 固定宽高
.background(Color.LightGray), // 背景色
contentAlignment = Alignment.Center // 子组件居中对齐
) {
Text(text = "Box 居中文本", color = Color.Black)
}
}
3. 核心参数说明
Box 的核心参数仅有 3 个,简洁且功能强大:
@Composable
fun Box(
modifier: Modifier = Modifier, // 布局修饰符(尺寸、背景、点击等)
contentAlignment: Alignment = Alignment.TopStart, // 子组件默认对齐方式
propagateMinConstraints: Boolean = false, // 是否传递最小约束给子组件
content: @Composable BoxScope.() -> Unit // 子组件内容
)
-
contentAlignment:默认TopStart(左上角),控制所有未单独指定对齐的子组件的位置; -
propagateMinConstraints:默认false,设为true时,Box 会将自身的最小尺寸约束传递给子组件(比如 Box 最小宽度 100dp,子组件也会至少 100dp)。
二、核心用法(实战高频)
1. 子组件对齐(基础中的基础)
(1)全局对齐(contentAlignment)
通过 contentAlignment 统一控制所有子组件的对齐位置:
@Composable
fun BoxAlignmentDemo() {
Box(
modifier = Modifier.size(200.dp).background(Color.LightGray),
contentAlignment = Alignment.BottomEnd // 所有子组件右下角对齐
) {
Text("右下角文本")
// 第二个子组件也会默认右下角对齐(层叠在第一个文本上方)
Text("层叠文本", color = Color.Red)
}
}
(2)单个子组件对齐(BoxScope.align ())
通过 Modifier.align() 为单个子组件指定独立对齐方式(覆盖全局 contentAlignment):
@Composable
fun BoxIndividualAlignment() {
Box(
modifier = Modifier.size(200.dp).background(Color.LightGray),
contentAlignment = Alignment.Center // 全局默认居中
) {
// 单个组件:左上角对齐(覆盖全局)
Text("左上角", modifier = Modifier.align(Alignment.TopStart))
// 单个组件:右下角对齐
Text("右下角", modifier = Modifier.align(Alignment.BottomEnd))
// 继承全局:居中
Text("居中")
}
}
(3)常用对齐方向(Alignment 枚举)
| 对齐值 | 位置 | 示例场景 |
|---|---|---|
Alignment.TopStart | 左上角 | 标题、头像角标 |
Alignment.TopCenter | 顶部居中 | 顶部提示文字 |
Alignment.TopEnd | 右上角 | 关闭按钮 |
Alignment.CenterStart | 左侧居中 | 列表项图标 |
Alignment.Center | 正中心 | 加载中提示、空页面文案 |
Alignment.CenterEnd | 右侧居中 | 按钮图标 |
Alignment.BottomStart | 左下角 | 版权信息 |
Alignment.BottomCenter | 底部居中 | 底部按钮栏 |
Alignment.BottomEnd | 右下角 | 操作按钮、角标 |
2. 层叠布局(Box 核心优势)
Box 最核心的能力是层叠子组件,后声明的组件会绘制在前面组件的上方,结合 alpha/background 可实现遮罩、叠加等效果。
示例 1:图片 + 文字遮罩
@Composable
fun BoxOverlayDemo() {
Box(
modifier = Modifier.size(200.dp),
contentAlignment = Alignment.BottomCenter
) {
// 底层:背景(模拟图片)
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Blue.copy(alpha = 0.7f))
)
// 上层:文字遮罩
Text(
text = "风景图片",
color = Color.White,
modifier = Modifier.padding(8.dp)
)
}
}
示例 2:按钮 + 角标(Badge)
@Composable
fun BoxBadgeDemo() {
Box {
// 底层:按钮
Button(
onClick = {},
modifier = Modifier.size(80.dp)
) {
Text("消息")
}
// 上层:角标(右上角)
Box(
modifier = Modifier
.size(20.dp)
.background(Color.Red, shape = androidx.compose.foundation.shape.CircleShape)
.align(Alignment.TopEnd),
contentAlignment = Alignment.Center
) {
Text("3", color = Color.White, fontSize = 12.sp)
}
}
}
3. 尺寸控制(Modifier 结合)
Box 的尺寸完全通过 Modifier 控制,支持多种尺寸策略:
@Composable
fun BoxSizeDemo() {
Column(Modifier.padding(16.dp)) {
// 1. 包裹内容(默认)
Box(
modifier = Modifier
.background(Color.LightGray)
.padding(8.dp)
) {
Text("包裹内容的 Box")
}
// 2. 固定尺寸
Box(
modifier = Modifier
.size(100.dp)
.background(Color.LightGray)
.padding(8.dp),
contentAlignment = Alignment.Center
) {
Text("固定 100dp")
}
// 3. 填充父布局(占满可用空间)
Box(
modifier = Modifier
.fillMaxWidth() // 占满宽度
.height(60.dp) // 固定高度
.background(Color.LightGray)
.padding(8.dp),
contentAlignment = Alignment.CenterStart
) {
Text("占满宽度,固定高度")
}
// 4. 比例尺寸(结合 aspectRatio)
Box(
modifier = Modifier
.fillMaxWidth()
.aspectRatio(16f / 9f) // 16:9 宽高比
.background(Color.LightGray)
.padding(8.dp),
contentAlignment = Alignment.Center
) {
Text("16:9 比例 Box")
}
}
}
三、BoxScope 专属修饰符(进阶)
Box 内部的子组件可使用 BoxScope 提供的专属修饰符,增强布局灵活性:
1. align ()(最常用)
为单个子组件指定对齐方式,前面已详细说明,核心是覆盖 Box 的全局 contentAlignment。
2. matchParentSize()
让子组件尺寸完全匹配 Box 的尺寸(即使子组件本身是 wrapContent),常用于层叠的背景 / 遮罩:
@Composable
fun BoxMatchParentSize() {
Box(
modifier = Modifier.size(150.dp).background(Color.Gray),
contentAlignment = Alignment.Center
) {
// 遮罩:匹配 Box 尺寸,半透明
Box(
modifier = Modifier
.matchParentSize()
.background(Color.Black.copy(alpha = 0.3f))
)
// 文字:居中显示
Text("带遮罩的内容", color = Color.White)
}
}
⚠️ 注意:matchParentSize() 仅在 Box 内生效,且子组件不能同时设置 fillMaxSize()(冲突)。
3. layoutPriority()-有问题
控制子组件的布局优先级(默认 0,值越大优先级越高),优先级高的组件先测量,优先占据空间:
@Composable
fun BoxLayoutPriority() {
Box(Modifier.size(100.dp).background(Color.LightGray)) {
// 优先级低(0):会被优先级高的组件覆盖
Text(
"低优先级",
modifier = Modifier
.size(80.dp)
.background(Color.Red)
.layoutPriority(0f)
)
// 优先级高(1):先测量,显示在上方
Text(
"高优先级",
modifier = Modifier
.size(50.dp)
.background(Color.Green)
.layoutPriority(1f)
.align(Alignment.Center)
)
}
}
四、高级实战场景
1. 加载中占位布局
@Composable
fun LoadingBoxDemo(isLoading: Boolean = true) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
.background(Color.LightGray),
contentAlignment = Alignment.Center
) {
// 正常内容(加载完成显示)
if (!isLoading) {
Text("加载完成:这是内容区域", color = Color.Black)
} else {
// 加载中:层叠显示进度条
CircularProgressIndicator()
}
}
}
2. 可点击卡片布局
@Composable
fun ClickableCardDemo() {
Box(
modifier = Modifier
.size(200.dp)
.background(Color.White, shape = androidx.compose.foundation.shape.RoundedCornerShape(16.dp))
.shadow(elevation = 4.dp, shape = androidx.compose.foundation.shape.RoundedCornerShape(16.dp))
.clickable { /* 点击事件 */ }
.padding(16.dp),
contentAlignment = Alignment.Center
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text("可点击卡片", fontSize = 18.sp, fontWeight = androidx.compose.ui.text.font.FontWeight.Bold)
Text("点击我触发事件", fontSize = 12.sp, color = Color.Gray, modifier = Modifier.padding(top = 8.dp))
}
}
}
3. 渐变背景 + 文字布局
@Composable
fun GradientBoxDemo() {
Box(
modifier = Modifier
.fillMaxWidth()
.height(150.dp)
.background(
brush = Brush.linearGradient(
colors = listOf(Color.Blue, Color.Yellow),
start = Offset.Zero,
end = Offset.Infinite
)
),
contentAlignment = Alignment.Center
) {
Text("渐变背景文本", color = Color.White, fontSize = 20.sp)
}
}
4. 列表项布局(结合 Row)
@Composable
fun ListItemBoxDemo() {
Box(
modifier = Modifier
.fillMaxWidth()
.height(80.dp)
.padding(8.dp)
.background(Color.White)
.clickable { /* 点击事件 */ }
) {
// 左侧图标
Box(
modifier = Modifier
.size(48.dp)
.background(Color.LightGray, shape = androidx.compose.foundation.shape.CircleShape)
.align(Alignment.CenterStart)
.padding(8.dp)
) {
androidx.compose.material.icons.Icons.Default.Person?.let {
androidx.compose.material3.Icon(
imageVector = it,
contentDescription = "头像",
modifier = Modifier.fillMaxSize()
)
}
}
// 中间文本
Column(
modifier = Modifier
.align(Alignment.CenterStart)
.padding(start = 64.dp)
) {
Text("用户名", fontSize = 16.sp)
Text("最后登录:2小时前", fontSize = 12.sp, color = Color.Gray)
}
// 右侧箭头
androidx.compose.material.icons.Icons.Default.ArrowDropDown?.let {
androidx.compose.material3.Icon(
imageVector = it,
contentDescription = "箭头",
modifier = Modifier
.size(20.dp)
.align(Alignment.CenterEnd)
.padding(end = 16.dp)
)
}
}
}
五、性能优化(避坑关键)
1. 减少不必要的层叠
- 仅当需要层叠时使用 Box,纯线性排列优先用 Row/Column(Box 层叠会增加绘制层级);
- 避免多层嵌套 Box(比如 Box 内套 Box 套 Box),可通过
Modifier合并逻辑。
2. 缓存不变的修饰符
- 避免在 Box 内部重复创建
Shape/Brush/Color等对象,否则会触发频繁重组:
// 错误:每次重组创建新的 Shape
Box(modifier = Modifier.background(Color.White, RoundedCornerShape(16.dp)))
// 正确:缓存 Shape
val cardShape = remember { RoundedCornerShape(16.dp) }
Box(modifier = Modifier.background(Color.White, cardShape))
3. 合理使用 matchParentSize ()
matchParentSize()会继承 Box 的尺寸,无需再给子组件设置fillMaxSize();- 仅层叠的背景 / 遮罩使用
matchParentSize(),普通内容优先用wrapContent/ 固定尺寸。
4. 避免过度使用 layoutPriority ()
layoutPriority()会改变布局测量顺序,增加计算耗时,仅在必要时使用(如层叠组件需要优先显示)。
六、常见问题 & 解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 子组件无法居中 | 未设置 contentAlignment 或 align() | 给 Box 设置 contentAlignment = Alignment.Center |
| 层叠组件显示顺序错误 | 声明顺序反了(后声明的在上方) | 调整子组件声明顺序 |
| matchParentSize () 无效 | 子组件同时设置了 fillMaxSize() | 移除 fillMaxSize(),仅保留 matchParentSize() |
| Box 高度为 0 | 子组件都是 wrapContent 且无内容 | 给 Box 设置固定高度或 minHeight |
| 点击事件不生效 | clickable 修饰符在背景之前 | 调整 Modifier 顺序:background 在前,clickable 在后 |