【共创季稿事节】鸿蒙原生ArkTS布局方式之Column+Expanded自适应布局
鸿蒙原生 ArkTS 布局方式之 Column + Expanded 自适应布局

一、引言
布局是构建用户界面的基石。HarmonyOS NEXT 提供了 ArkTS 声明式 UI 框架,其中 Column、Row、Flex、Stack 等布局容器构成了灵活多变的布局体系。Column + Expanded 是最基础、最能体现弹性设计思想的组合之一。
本文将以其完整示例为线索,深入剖析布局原理、核心属性、最佳实践及常见陷阱,帮助开发者从零掌握这一布局方式。
二、Column 布局容器
2.1 什么是 Column
Column 是 ArkTS 中最基础的垂直方向布局容器,将子组件沿垂直方向(从上到下)依次排列,类似于 CSS Flexbox 的 flex-direction: column。Column 属于线性布局,底层基于 Flex 弹性盒模型实现。
2.2 基本使用
@Entry
@Component
struct ColumnExample {
build() {
Column() {
Text('子组件 1').fontSize(16)
Text('子组件 2').fontSize(16)
Text('子组件 3').fontSize(16)
}
.width('100%').height('100%')
}
}
2.3 核心属性
| 属性 | 类型 | 说明 |
|---|---|---|
alignItems |
HorizontalAlign |
子组件水平对齐方式 |
justifyContent |
FlexAlign |
子组件垂直分布方式 |
width/height |
Length |
容器宽高 |
padding |
Padding |
内边距 |
space |
Length |
子组件间距 |
注意:使用 Expanded 时 justifyContent 失效,因为 Expanded 会自动填充剩余空间。
2.4 适用场景
页面整体结构划分、表单垂直排列、列表项单列布局、与 Scroll 配合实现可滚动内容。
三、Expanded 组件
3.1 作用与原理
Expanded 是 ArkTS 中实现弹性伸缩的核心组件,包裹在 Column 或 Row 的子组件外层,使被包裹的子组件自动撑满容器中剩余的空白空间。Expanded 不渲染任何视觉内容,只负责分配空间。
3.2 核心属性:layoutWeight
layoutWeight 用于在多个 Expanded 子组件之间按比例分配剩余空间。
Column() {
Expanded() { /* 区域 A */ }.layoutWeight(1)
Expanded() { /* 区域 B */ }.layoutWeight(2)
Expanded() { /* 区域 C */ }.layoutWeight(1)
}
权重比 1:2:1。设两个固定组件共 140vp,屏幕高 852vp,则剩余 712vp 分配:
- 区域 A:712 × (1÷4) = 178vp
- 区域 B:712 × (2÷4) = 356vp
- 区域 C:712 × (1÷4) = 178vp
3.3 默认值与注意事项
未显式设置时默认值为 1。混合使用时,不带权重的也按 1 参与计算。关键注意点:
| 注意点 | 说明 |
|---|---|
| 必须配合 Column/Row | 在其他容器中无效 |
| 父容器必须有明确高度 | 如 height('100%') |
| 内部组件需设 100% 宽高 | 否则无法视觉填满 |
| 不与 justifyContent 混用 | Expanded 会覆盖其效果 |
四、完整示例代码分析
4.1 示例代码
@Entry
@Component
struct Index {
@State pageTitle: string = 'Column + Expanded 自适应布局';
build() {
Column() {
// 1. 顶部标题 —— 固定高度 60vp
Text(this.pageTitle)
.fontSize(20).fontWeight(FontWeight.Bold).fontColor(Color.White)
.textAlign(TextAlign.Center).width('100%').height(60)
.backgroundColor('#2E4E7E').borderRadius({ bottomLeft: 12, bottomRight: 12 }).padding(10)
// 2. 区域 A —— layoutWeight = 1
Expanded() {
Row() {
Text('layoutWeight = 1').fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Medium)
}.width('100%').height('100%').justifyContent(FlexAlign.Center).backgroundColor('#43A047')
}.layoutWeight(1)
// 3. 区域 B —— layoutWeight = 2
Expanded() {
Column() {
Text('layoutWeight = 2').fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Medium)
Text('权重是左右两块的 2 倍').fontSize(14).fontColor('#FFFFFFCC').margin({ top: 8 })
}.width('100%').height('100%').justifyContent(FlexAlign.Center)
.alignItems(HorizontalAlign.Center).backgroundColor('#F57C00')
}.layoutWeight(2)
// 4. 区域 C —— layoutWeight = 1
Expanded() {
Row() {
Text('layoutWeight = 1').fontSize(18).fontColor(Color.White).fontWeight(FontWeight.Medium)
}.width('100%').height('100%').justifyContent(FlexAlign.Center).backgroundColor('#8E24AA')
}.layoutWeight(1)
// 5. 底部说明 —— 固定高度 80vp
Column() {
Text('布局说明').fontSize(16).fontWeight(FontWeight.Bold).fontColor('#333333')
Text('Column + Expanded + layoutWeight').fontSize(13).fontColor('#666666')
Text('Expanded 子项按 layoutWeight 比例撑满剩余空间').fontSize(13).fontColor('#666666')
}.width('100%').height(80).backgroundColor('#F5F5F5')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).padding(10)
}
.width('100%').height('100%').alignItems(HorizontalAlign.Center)
}
}
4.2 布局结构拆解
┌──────────────────────────────────┐
│ Column + Expanded 自适应布局 │ ← 固定 60vp,深蓝
├──────────────────────────────────┤
│ layoutWeight = 1 │ ← 权重 1,绿色,弹性区 1/4
├──────────────────────────────────┤
│ layoutWeight = 2 │ ← 权重 2,橙色,弹性区 2/4
│ 权重是左右两块的 2 倍 │
├──────────────────────────────────┤
│ layoutWeight = 1 │ ← 权重 1,紫色,弹性区 1/4
├──────────────────────────────────┤
│ 布局说明 · Column+Expanded+… │ ← 固定 80vp,浅灰
└──────────────────────────────────┘
4.3 代码设计要点
(1)视觉分区明确: 每个区域使用不同背景色(深蓝、绿、橙、紫、浅灰)区分边界。
(2)固定与弹性分离: 标题 60vp 和底栏 80vp 固定高度,弹性区在 Expanded 内。这种「固定 — 弹性 — 固定」结构是经典应用模式。
(3)权重比例可观察: 区域 A 和 C 权重同为 1,B 权重为 2,运行时橙色区高度恰为绿/紫区的两倍。
(4)显式指定权重: 虽默认值为 1,但显式写出 .layoutWeight(1/2) 提高了代码可读性。
五、自适应布局的核心价值
5.1 什么是自适应布局
鸿蒙生态中设备形态多样:折叠屏展开前后、平板横竖屏切换、不同手机分辨率、车机屏幕各异。自适应布局(Adaptive Layout)使页面能根据屏幕空间自动调整。Column + Expanded 是实现垂直自适应的最直接手段。
5.2 解决的问题
| 问题 | 传统做法 | Column + Expanded 方案 |
|---|---|---|
| 不同屏幕高度下内容断层 | 固定 px 硬编码 | 按比例自动填充 |
| 横竖屏切换布局失衡 | 两套布局文件 | 一套布局自动适应 |
| 折叠屏展开后空白 | 额外适配 | Expanded 自动拉伸 |
| 多端公用页面 | 多套代码维护 | 一套代码自适应 |
5.3 布局容器选型
| 布局 | 适合场景 | 不适合 |
|---|---|---|
| Column + Expanded | 垂直按比例分配 | 二维网格、层叠 |
| Row + Expanded | 水平按比例分配 | 垂直布局 |
| Stack | 层叠覆盖 | 线性排列 |
| Flex | 灵活轴线+换行 | 简单垂直 |
| RelativeContainer | 锚定对齐 | 比例分配 |
| Grid | 二维网格 | 非规则伸缩 |
| List | 长列表 | 固定比例 |
六、layoutWeight 进阶用法
6.1 非整数权重
支持浮点数实现精细比例控制。
Expanded() { /* 左 */ }.layoutWeight(1)
Expanded() { /* 右 */ }.layoutWeight(1.618) // 黄金比例
6.2 嵌套使用
Expanded 内可继续嵌套 Column/Row 和 Expanded,实现多级比例分配。
Column() {
Expanded() {
Row() {
Expanded() { /* 左 30% */ }.layoutWeight(1)
Expanded() { /* 右 70% */ }.layoutWeight(2.33)
}
}.layoutWeight(2)
Expanded() { /* 下方区域 */ }.layoutWeight(3)
}
6.3 与固定尺寸混合
Column() {
ToolBar().height(56)
Expanded() { ContentArea() }.layoutWeight(1)
StatusBar().height(32)
}
七、实际项目应用模式
7.1 经典三段式
Column() {
TitleBar().height(56)
Expanded() { ContentArea() }.layoutWeight(1)
BottomTabBar().height(64)
}
7.2 等分与主次
// 等分卡片
Column() {
Expanded() { CardA() }.layoutWeight(1)
Expanded() { CardB() }.layoutWeight(1)
}
// 主次区域(聊天界面)
Column() {
Expanded() { MessageList() }.layoutWeight(3)
Expanded() { InputArea() }.layoutWeight(1)
}
7.3 横纵结合
Column() {
Expanded() {
Row() {
Expanded() { LeftPanel() }.layoutWeight(1)
Divider().width(1).height('100%').backgroundColor('#CCC')
Expanded() { RightPanel() }.layoutWeight(2)
}.width('100%').height('100%')
}.layoutWeight(1)
StatusBar().height(32)
}
7.4 配合 Scroll 和 List
// Scroll(Expanded 在外,Scroll 在内)
Column() {
Expanded() {
Scroll() {
Column() { ForEach(this.data, (item) => { ListItem(item) }) }
}.scrollBar(BarState.Off)
}.layoutWeight(1)
FixedBar().height(60)
}
// List
Column() {
Expanded() {
List() {
ForEach(this.list, (item) => { ListItem() { ItemCard(item) } })
}.width('100%').height('100%')
}.layoutWeight(1)
}
八、性能分析
8.1 布局计算
单次线性遍历,时间复杂度 O(n)。RelativeContainer(锚点约束更高)、Flex 带 wrap(换行计算更高)、Grid(二维计算更高)。
8.2 建议
| 指标 | 建议 |
|---|---|
| Expanded 数量 | 不超过 10 个 |
| 嵌套层级 | 不超过 5 层 |
| 动态更新 | 支持响应式,频繁更新会触发重排 |
8.3 优化建议
- 使用
@Builder提取可复用布局 - 使用
LazyForEach懒加载 - Expanded 内避免大量同级组件,优先用 List
- 合理使用
@Prop/@Link控制更新范围
九、常见陷阱与解决方案
9.1 Expanded 不生效
原因: 父容器 Column 未设置 height('100%')。
解决:
Column() { Expanded() { Text('内容') } }.width('100%').height('100%')
9.2 内部子组件未占满
原因: 子组件未设置 100% 宽高。
解决: 设置 .width('100%').height('100%')。
9.3 Scroll 内使用 Expanded
原因: Scroll 提供无限空间,Expanded 无法确定边界。
解决: Expanded 在外,Scroll 在内。见 7.4 节。
9.4 不同设备高度不一致
原因: 安全区域高度不同。
解决: 使用 expandSafeArea()。
Column().width('100%').height('100%').expandSafeArea()
9.5 嵌套权重混乱
原因: 内外层权重独立分层计算,外层决定整屏比例,内层决定容器内比例。二者互不干扰。
十、与状态管理联动
10.1 响应式权重
layoutWeight 可绑定 @State 变量。
@Entry
@Component
struct DynamicPage {
@State left: number = 1
@State right: number = 1
build() {
Column() {
Expanded() {
Text('左侧').fontSize(24).fontColor(Color.White)
.width('100%').height('100%').textAlign(TextAlign.Center)
.backgroundColor('#1565C0')
}.layoutWeight(this.left)
Expanded() {
Text('右侧').fontSize(24).fontColor(Color.White)
.width('100%').height('100%').textAlign(TextAlign.Center)
.backgroundColor('#C62828')
}.layoutWeight(this.right)
}.width('100%').height('100%')
}
}
10.2 动态隐藏/展开
@Entry
@Component
struct CollapsiblePanel {
@State expanded: boolean = true
build() {
Column() {
Expanded() {
Text('主内容').width('100%').height('100%').backgroundColor('#E8F5E9')
}.layoutWeight(1)
if (this.expanded) {
Expanded() {
Column() {
Text('折叠面板').fontSize(20).fontColor(Color.White)
Text('额外信息').fontSize(14).fontColor('#FFFFFFCC')
}.width('100%').height('100%')
.justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)
.backgroundColor('#37474F')
}.layoutWeight(1)
}
}.width('100%').height('100%')
}
}
十一、不同设备形态
| 设备形态 | 表现 | 建议 |
|---|---|---|
| 手机竖屏 | 最自然,高度充足 | 默认布局 |
| 手机横屏 | 弹性区自动缩小,不断裂 | 可配合媒体查询 |
| 平板/折叠屏展开 | 弹性区等比例拉伸 | 零修改适配 |
| 车机/智慧屏 | 大屏内容过度拉伸 | 配合 constraintSize 限制宽度 |
Column() {
Expanded() { ContentArea().constraintSize({ maxWidth: 600 }) }.layoutWeight(1)
}.alignItems(HorizontalAlign.Center).width('100%').height('100%')
十二、编码规范
12.1 命名与注释
- 组件后缀使用
Page、Panel、Section - 注释标注权重比例
// 主内容 : 侧边栏 = 2 : 1
Expanded() { MainContent() }.layoutWeight(2)
Expanded() { Sidebar() }.layoutWeight(1)
12.2 组件抽象
Expanded 子组件逻辑复杂时抽取为独立组件。
// ✅ 推荐
Expanded() { ProfileCard() }.layoutWeight(1)
12.3 测试建议
- 不同屏幕尺寸模拟器验证比例
- 横竖屏切换验证稳定性
- 测试动态修改 layoutWeight 的过渡
- 确保 Expanded 内无固定高度组件覆盖弹性行为
十三、总结
Column+Expanded 是鸿蒙 ArkTS 最实用的布局组合之一,以简单规则实现灵活空间分配,自适应多种设备形态。
核心要义:
- Column 提供垂直排列框架,
.width('100%').height('100%')确保容器有明确边界。 - Expanded 按
layoutWeight权重比例分配剔除固定组件后的剩余空间。 - layoutWeight 支持整数和浮点数,实现任意比例分配,支持响应式更新。
- 与固定尺寸组件混用是核心设计模式,顶栏+弹性区+底栏的三段式是最经典应用。
- 注意陷阱:父容器必须有明确高度、内部子组件需设 100% 宽高、Scroll 内不能直接使用 Expanded。
在 HarmonyOS NEXT 推进多端协同的背景下,掌握 Column + Expanded 是理解鸿蒙设计哲学的重要一步——以弹性应对变化,以比例连接万物。
附录:参考资料
- 华为开发者官网:HarmonyOS NEXT ArkTS 布局开发指南
@Component和@Entry装饰器使用说明- Column 组件 API 参考文档
- Expanded 组件 API 参考文档
- layoutWeight 属性详解
- 鸿蒙自适应布局最佳实践
更多推荐




所有评论(0)