Compose 布局
问题
Compose 的布局系统是如何工作的?常用的布局组件有哪些?
答案
1. 基础布局组件
| 组件 | 说明 | 类比 View |
|---|---|---|
Column | 垂直排列 | LinearLayout(vertical) |
Row | 水平排列 | LinearLayout(horizontal) |
Box | 层叠布局 | FrameLayout |
ConstraintLayout | 约束布局 | ConstraintLayout |
LazyColumn | 垂直懒加载列表 | RecyclerView(vertical) |
LazyRow | 水平懒加载列表 | RecyclerView(horizontal) |
LazyGrid | 网格懒加载列表 | RecyclerView(Grid) |
@Composable
fun ProfileCard() {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 头像
Image(
painter = painterResource(R.drawable.avatar),
contentDescription = "头像",
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
)
Spacer(modifier = Modifier.width(12.dp))
// 信息
Column {
Text("Alice", style = MaterialTheme.typography.titleMedium)
Text("Android Developer", style = MaterialTheme.typography.bodySmall)
}
}
}
2. LazyColumn(列表)
@Composable
fun UserList(users: List<User>) {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
// 头部
item { Text("用户列表", style = MaterialTheme.typography.headlineSmall) }
// 列表项(key 用于优化重组和动画)
items(users, key = { it.id }) { user ->
UserItem(user)
}
// 尾部加载更多
item { LoadingIndicator() }
}
}
3. Compose 布局原理
Compose 布局遵循单次测量原则(与 View 体系不同,View 允许多次测量):
每个布局节点只能测量子节点一次,这保证了 的布局时间复杂度(View 体系中 RelativeLayout 可能 )。
4. 自定义布局
// 使用 Layout Composable 自定义布局
@Composable
fun CustomFlowLayout(
modifier: Modifier = Modifier,
spacing: Dp = 8.dp,
content: @Composable () -> Unit
) {
Layout(content = content, modifier = modifier) { measurables, constraints ->
val spacingPx = spacing.roundToPx()
// 1. 测量所有子元素
val placeables = measurables.map { it.measure(constraints) }
// 2. 计算位置(换行逻辑)
var x = 0
var y = 0
var lineHeight = 0
val positions = placeables.map { placeable ->
if (x + placeable.width > constraints.maxWidth) {
x = 0
y += lineHeight + spacingPx
lineHeight = 0
}
val pos = Pair(x, y)
x += placeable.width + spacingPx
lineHeight = maxOf(lineHeight, placeable.height)
pos
}
// 3. 放置
layout(constraints.maxWidth, y + lineHeight) {
placeables.forEachIndexed { i, placeable ->
placeable.placeRelative(positions[i].first, positions[i].second)
}
}
}
}
常见面试问题
Q1: LazyColumn 和 Column 的区别?
答案:
Column:一次性渲染所有子元素。适合子元素数量少的场景LazyColumn:按需渲染可见区域的子元素,回收不可见的。适合长列表
LazyColumn 类似 RecyclerView,但不需要 Adapter/ViewHolder,代码更简洁。
Q2: Compose 布局中为什么不允许多次测量子元素?
答案:
多次测量会导致布局性能从 退化到 。Compose 强制单次测量来保证性能。
如果需要在第一次测量后根据结果调整,可以使用 SubcomposeLayout(延迟到布局阶段才 compose 子元素,可以根据其他子元素的测量结果决定 compose 什么)。典型应用:Scaffold 中 FloatingActionButton 的位置。
Q3: Arrangement 和 Alignment 有什么区别?
答案:
Arrangement:控制子元素在主轴上的分布方式(间距、对齐)Column→verticalArrangement(如Arrangement.spacedBy(8.dp))Row→horizontalArrangement(如Arrangement.SpaceBetween)
Alignment:控制子元素在交叉轴上的对齐方式Column→horizontalAlignment(如Alignment.CenterHorizontally)Row→verticalAlignment(如Alignment.CenterVertically)
Q4: fillMaxSize 和 wrapContentSize 有什么区别?
答案:
Modifier.fillMaxSize():填充父容器允许的最大空间,可传入fraction参数控制百分比Modifier.wrapContentSize():按内容大小包裹
这类似 View 体系中的 match_parent 和 wrap_content。