D1布局三件套

Column / Row / Box 是拼界面的底座。绝大多数常见页,先用它们搭骨架,再用Modifier调尺寸边距。

📌 示例边界:下列 Kotlin 片段都写在某个 @Composable 函数体内。尺寸、边距等 Modifier 链式写法下一章细讲,本章只带最常用的。

Column ★

概念

Column 把子项从上到下排成一列。像竖着的货架:标题、表单项、按钮依次往下叠。

和 XML 对照

ComposeXML 近似
ColumnLinearLayout + android:orientation="vertical"
verticalArrangementgravity / 间距靠 margin 硬撑
horizontalAlignment子 View 的 layout_gravity / gravity

怎么用

★ 常用参数:

  • modifier — 整列的尺寸、边距、背景
  • verticalArrangement — 子项在竖直方向怎么分布(顶对齐、均分、底部、间距)
  • horizontalAlignment — 子项在水平方向相对列怎么齐(左 / 居中 / 右)
@Composable
fun ColumnBasics() {
    Column(
        modifier = Modifier
            .fillMaxWidth()
            .padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(12.dp),
        horizontalAlignment = Alignment.Start
    ) {
        Text("标题")
        Text("说明文字")
        Button(onClick = { }) {
            Text("主按钮")
        }
    }
}

易错点

  • 内容超出屏幕却不滚动——长页要在 Column 上挂滚动(或改用 Lazy),别指望默认能滑。
  • 把「子项之间的间距」全靠每个子项自己 padding——优先 Arrangement.spacedBy,更整齐。
▶ 设置页:标题 + 一列表单项

设置页典型骨架:顶上标题,下面一列「标签 + 说明」行。XML 常用竖向 LinearLayout;Compose 用 Column + 行内再用 Row(见下一节)。

@Composable
fun SettingsSkeleton() {
    Column(
        modifier = Modifier
            .fillMaxSize()
            .padding(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        Text("设置", style = MaterialTheme.typography.titleLarge)
        SettingsItem(title = "账号", subtitle = "登录与安全")
        SettingsItem(title = "通知", subtitle = "推送与提醒")
        SettingsItem(title = "关于", subtitle = "版本与协议")
    }
}

@Composable
fun SettingsItem(title: String, subtitle: String) {
    Column(Modifier = Modifier.fillMaxWidth()) {
        Text(title)
        Text(subtitle, color = MaterialTheme.colorScheme.onSurfaceVariant)
    }
}

要点:外层 Column 管「整页竖排」;每一行可以是更小的 Column(两行字)或 Row(左文右箭头)。

Row ★

概念

Row 把子项从左到右排成一行。列表行、工具栏、按钮组,几乎都先想到它。

和 XML 对照

ComposeXML 近似
RowLinearLayout + orientation="horizontal"
horizontalArrangement水平 gravity / 间距
verticalAlignment子项垂直方向 gravity
Modifier.weight(1f)layout_weight

怎么用

★ 常用参数:

  • horizontalArrangement — 水平分布(靠左、两端对齐、均分、间距)
  • verticalAlignment — 行内垂直对齐(顶 / 居中 / 底)
  • Modifier.weight仅在 Row/Column 直接子项上按比例分剩余空间(≈ layout_weight
@Composable
fun RowBasics() {
    Row(
        modifier = Modifier.fillMaxWidth(),
        horizontalArrangement = Arrangement.SpaceBetween,
        verticalAlignment = Alignment.CenterVertically
    ) {
        Text("左侧")
        Text("右侧")
    }
}

@Composable
fun RowWithWeight() {
    Row(modifier = Modifier.fillMaxWidth()) {
        Text(
            "占满剩余",
            modifier = Modifier.weight(1f)
        )
        Text("固定尾")
    }
}

易错点

  • weight 写在非 Row/Column 直接子项上——无效;必须是该作用域里的直接孩子。
  • 想「中间文字撑开、两边图标固定」却忘了给中间项 weight(1f)
  • 行内多行文字垂直不齐——给 Row 设 verticalAlignment = Alignment.CenterVertically
▶ 列表行:头像 + 两行文字 + 尾箭头

经典列表 cell:左圆形头像,中间标题+副标题竖排,右侧箭头。XML 常是横向 LinearLayout 嵌套;Compose 是 Row 里塞 Column

@Composable
fun ListRowDemo(
    title: String,
    subtitle: String,
    onClick: () -> Unit
) {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .clickable(onClick = onClick)
            .padding(horizontal = 16.dp, vertical = 12.dp),
        verticalAlignment = Alignment.CenterVertically
    ) {
        // 头像占位:实战换成 Image / AsyncImage
        Box(
            modifier = Modifier
                .size(40.dp)
                .background(MaterialTheme.colorScheme.primaryContainer)
        )
        Column(
            modifier = Modifier
                .weight(1f)
                .padding(horizontal = 12.dp)
        ) {
            Text(title)
            Text(
                subtitle,
                color = MaterialTheme.colorScheme.onSurfaceVariant
            )
        }
        Text("›")
    }
}

要点:中间 Columnweight(1f) 吃掉剩余宽度,箭头才会被顶到最右。

Box ★

概念

Box 让子项叠在同一区域(后写的默认画在上面)。适合底图+角标、内容+加载圈、全屏蒙层。

和 XML 对照

ComposeXML 近似
BoxFrameLayout
子项 Modifier.align(...)layout_gravity
contentAlignment默认把未单独 align 的子项往哪靠

怎么用

  • contentAlignment — 没单独指定 align 的子项,默认对齐点(常用来整页居中一个加载圈)
  • 子项 Modifier.align(Alignment.TopEnd) 等 — 在 Box 里精确定位(★)
@Composable
fun BoxBasics() {
    Box(
        modifier = Modifier.size(120.dp),
        contentAlignment = Alignment.Center
    ) {
        Text("底层")
        Text(
            "角标",
            modifier = Modifier.align(Alignment.TopEnd)
        )
    }
}

易错点

  • 想居中却用一堆 padding「顶」——在 Box 里用 align / contentAlignment
  • 把 Box 当成万能容器乱套——能横排用 Row、能竖排用 Column,Box 留给「需要叠」的场景。
  • 叠放顺序:写在后面的子项默认更靠上;必要时再用 zIndex(进阶)。
▶ 图片上叠加载圈 / 角标

封面图加载中要转圈,加载完显示角标「NEW」。XML 用 FrameLayout;Compose 用 Box 叠三层。

@Composable
fun CoverWithOverlay(
    loading: Boolean,
    showBadge: Boolean
) {
    Box(
        modifier = Modifier.size(160.dp),
        contentAlignment = Alignment.Center
    ) {
        // 底图占位:实战换成 Image / AsyncImage
        Box(
            modifier = Modifier
                .matchParentSize()
                .background(MaterialTheme.colorScheme.surfaceVariant)
        )
        if (loading) {
            CircularProgressIndicator()
        }
        if (showBadge) {
            Text(
                "NEW",
                modifier = Modifier
                    .align(Alignment.TopEnd)
                    .padding(8.dp)
            )
        }
    }
}

要点:matchParentSize() 让底图铺满 Box;加载圈靠 contentAlignment 居中;角标单独 align(TopEnd)

何时用谁

概念

先问「子项之间的关系是竖、横,还是叠」,再选容器。复杂界面 = 三者嵌套,而不是找一个「超级布局」。

和 XML 对照

需求XML 常选Compose 常选
从上到下竖向 LinearLayoutColumn
从左到右横向 LinearLayoutRow
叠在一起FrameLayoutBox
复杂约束网ConstraintLayout先嵌套三件套;真不够再 Constraint(扩展)

怎么用(选型口诀)

  • 设置页 / 表单 → 先 Column
  • 列表一行 / 顶栏按钮 → 先 Row
  • 角标、加载遮罩、悬浮在内容上 → 先 Box
  • 一行里既有横排又有两行字 → Row { 头像; Column { 标题; 副标题 } }

易错点

  • 一上来 ConstraintLayout「保险」——日常页用三件套嵌套更清晰,Modifier 下一章补齐。
  • 嵌套过深(五六层)——把重复块抽成小 @Composable,而不是继续往里塞。
▶ 一个简单卡片头:Box + Row + Column

卡片头:圆角底(Box)上放一行(Row):左边头像+两行字(Column),右边操作按钮。

@Composable
fun CardHeaderDemo() {
    Box(
        modifier = Modifier
            .fillMaxWidth()
            .background(MaterialTheme.colorScheme.surfaceVariant)
            .padding(16.dp)
    ) {
        Row(verticalAlignment = Alignment.CenterVertically) {
            Box(
                modifier = Modifier
                    .size(48.dp)
                    .background(MaterialTheme.colorScheme.primary)
            )
            Column(
                modifier = Modifier
                    .weight(1f)
                    .padding(horizontal = 12.dp)
            ) {
                Text("昵称")
                Text("一句话简介")
            }
            TextButton(onClick = { }) {
                Text("关注")
            }
        }
    }
}

要点:Box 当「底板」;Row 管水平分区;Column 管文字竖排。尺寸圆角等细节留给下一章 Modifier。

下一章:Modifier——给三件套和组件挂尺寸、边距、点击、背景。尺寸/weight/align 会和本章对照出现在属性总表
Android Compose 现代开发知识体系 · 主线必读 / 扩展选读。
术语表常驻;组件与属性先看分讲,再查两张总表。案例默认收起。