鸿蒙原生ArkTS布局方式之FlexColumn弹性布局

一、前言

1.1 关于 HarmonyOS NEXT

HarmonyOS NEXT(鸿蒙星河版)是华为完全自主研发的操作系统,去除了 Android AOSP 代码,使用纯鸿蒙内核,应用只能通过 HarmonyOS 原生格式(HAP)安装运行。它标志着一个独立于 Android 与 iOS 之外的第三大移动生态系统的正式起航。

HarmonyOS NEXT 的应用开发语言是 ArkTS(Ark TypeScript),它在 TypeScript 的基础上做了大量扩展与限制,使其更适合声明式 UI 开发范式。ArkTS 提供了 @Component 和 @Entry 装饰器来定义页面组件,并通过链式调用的方式配置组件样式与行为。

在这里插入图片描述

1.2 布局在鸿蒙开发中的重要性

在任何一个 UI 系统中,布局(Layout)都是构建用户界面的基石。合理的布局能够让应用在不同屏幕尺寸、不同分辨率下呈现出协调一致的视觉效果。HarmonyOS NEXT 提供了丰富而灵活的布局组件,包括:

Flex:弹性布局,基于 CSS Flexbox 模型,是构建响应式界面的核心工具。
Column / Row:简化的垂直/水平线性布局,底层基于 Flex。
Stack:层叠布局,子组件按顺序堆叠。
RelativeContainer:相对布局,子组件之间通过锚点建立相对位置关系。
Grid:网格布局,将容器划分为行和列来排列子组件。
List:列表布局,用于构建长列表或可滚动内容。
Scroll:滚动容器,可包裹其他布局实现滚动效果。
在这些布局方式中,Flex 弹性布局 是最灵活、最常用的一种。无论是简单的一维排列还是复杂的自适应布局,Flex 都能优雅地胜任。本文我们将深入探讨 Flex + flexDirection(FlexDirection.Column) 这种垂直弹性布局方式,即 FlexColumn。

二、Flex 弹性布局基础

在这里插入图片描述

2.1 什么是 Flex 布局

Flex 是 “Flexible Box”(弹性盒子)的缩写。它是一种一维布局模型,可以在主轴(Main Axis)和交叉轴(Cross Axis)两个方向上对子组件进行灵活排列与分配。

与传统的线性布局相比,Flex 布局的核心优势在于:

弹性伸缩:子组件可以根据可用空间自动扩张或收缩。
对齐灵活:支持沿主轴和交叉轴的多维对齐方式。
顺序控制:可以通过 order 属性改变子组件的视觉排列顺序。
自适应:无需固定宽高,即可实现响应式效果。

2.2 主轴与交叉轴

理解 Flex 布局的关键在于理解 主轴(Main Axis) 和 交叉轴(Cross Axis) 这两个概念:

主轴:Flex 容器中子组件排列的方向。通过 flexDirection 属性控制。
交叉轴:垂直于主轴的另一个方向。
当 flexDirection 设置为 FlexDirection.Column 时:

维度 方向 对应 CSS 属性
主轴 垂直方向(从上到下) flex-direction: column
交叉轴 水平方向(从左到右) align-items 控制
当 flexDirection 设置为 FlexDirection.Row(默认值)时:

维度 方向 对应 CSS 属性
主轴 水平方向(从左到右) flex-direction: row
交叉轴 垂直方向(从上到下) align-items 控制

2.3 Flex 在 ArkTS 中的基本用法

在 ArkTS 中,Flex 是一个内置组件,通过构造参数 FlexOptions 来配置布局行为:

Flex({
  direction: FlexDirection.Column,      // 主轴方向:垂直
  wrap: FlexWrap.NoWrap,               // 是否换行
  justifyContent: FlexAlign.Start,     // 主轴对齐方式
  alignItems: ItemAlign.Center,        // 交叉轴对齐方式
  alignContent: FlexAlign.Start        // 多行/多列时的对齐方式
}) {
  // 子组件
}

FlexOptions 参数一览
参数名 类型 默认值 说明
direction FlexDirection FlexDirection.Row 主轴方向(Row / Column / RowReverse / ColumnReverse)
wrap FlexWrap FlexWrap.NoWrap 是否换行
justifyContent FlexAlign FlexAlign.Start 主轴对齐方式
alignItems ItemAlign ItemAlign.Auto 交叉轴对齐方式
alignContent FlexAlign FlexAlign.Start 多轴时对齐方式

三、FlexColumn 核心技术详解

3.1 flexDirection: FlexDirection.Column

这是 FlexColumn 布局的核心配置。当我们设置 direction: FlexDirection.Column 后,Flex 容器的排列方向从默认的水平变为垂直。

Flex({ direction: FlexDirection.Column }) {
  // 子组件从上往下排列
}

效果等价于:

Android 中的 LinearLayout 设置 orientation=“vertical”
CSS 中的 display: flex; flex-direction: column;
Flutter 中的 Column widget
FlexDirection 枚举值
枚举值 主轴方向 描述
FlexDirection.Row 水平(从左到右) 默认值
FlexDirection.Column 垂直(从上到下) 本文重点
FlexDirection.RowReverse 水平反向(从右到左) 子项反向水平排列
FlexDirection.ColumnReverse 垂直反向(从下到上) 子项反向垂直排列

3.2 flexGrow —— 弹性扩张

flexGrow 定义了当 Flex 容器在主轴方向上有剩余空间时,子组件按比例分配这些空间的能力。

取值: 非负整数(默认 0)

工作原理:

先计算所有子组件的原始尺寸之和。
如果原始尺寸之和小于容器尺寸,则产生剩余空间。
将所有设置了 flexGrow > 0 的子组件的 flexGrow 值相加得到总权重。
每个子组件按自己的 flexGrow 值与总权重的比例分配剩余空间。
示例分析:

Flex 容器高度:250px,内边距 24px(上下各 12px)
可用高度:250 - 24 = 226px

三个子组件原始高度:50px + 50px + 50px = 150px
剩余空间:226 - 150 = 76px

flexGrow 设置:1, 2, 0
总权重:1 + 2 + 0 = 3

分配结果:
子项 A(flexGrow=1):50 + 76 × (1/3) ≈ 50 + 25.3 = 75.3px
子项 B(flexGrow=2):50 + 76 × (2/3) ≈ 50 + 50.7 = 100.7px
子项 C(flexGrow=0):50px(不扩张)

3.3 flexShrink —— 弹性收缩

flexShrink 定义了当 Flex 容器在主轴上空间不足时,子组件按比例压缩的能力。

取值: 非负整数(默认 1)

工作原理:

先计算所有子组件的原始尺寸之和。
如果原始尺寸之和大于容器尺寸,产生溢出空间。
将设置了 flexShrink > 0 的子组件的 flexShrink 值乘以原始高度得到权重。
每个子组件按权重比例承担压缩量。
示例分析:

Flex 容器高度:200px,内边距 24px(上下各 12px)
可用高度:200 - 24 = 176px

三个子组件原始高度:120px + 120px + 120px = 360px
溢出空间:360 - 176 = 184px

flexShrink 设置:0, 1, 2
权重计算:
子项 A:0 × 120 = 0
子项 B:1 × 120 = 120
子项 C:2 × 120 = 240
总权重:0 + 120 + 240 = 360

压缩结果:
子项 A(flexShrink=0):120px(不压缩)
子项 B(flexShrink=1):120 - 184 × (120/360) = 120 - 61.3 = 58.7px
子项 C(flexShrink=2):120 - 184 × (240/360) = 120 - 122.7 = -2.7px → 实际会压缩到最小内容高度
注意: 在 ArkTS 中,flexShrink 的行为与 CSS 标准一致。当压缩后子项高度为负值时,实际显示会以子项内容的固有最小高度为下限。

3.4 flexBasis —— 基准尺寸

flexBasis 设置在分配弹性空间之前子组件的初始主轴尺寸。在 Column 方向下,即初始高度。

Text(‘设置基准高度’)
.flexBasis(100) // 初始高度为 100px
.flexGrow(1) // 在此基础上弹性扩张
flexBasis 与 height 的区别:

特性 flexBasis height
优先级 更高(在弹性计算中优先使用) 更低(会被 flexBasis 覆盖)
作用范围 仅在 Flex 容器内有效 通用属性,任何组件可用
与弹性关系 作为弹性伸缩的基准值 固定值,弹性伸缩忽略它

3.5 justifyContent —— 主轴对齐

justifyContent 控制子组件在主轴(对于 FlexColumn 即垂直方向)上的对齐方式。

在 ArkTS 中,该属性使用 FlexAlign 枚举:

FlexAlign 值 效果 说明
FlexAlign.Start 顶部对齐 子组件从容器顶部开始排列
FlexAlign.Center 垂直居中 子组件在容器垂直方向居中
FlexAlign.End 底部对齐 子组件靠近容器底部排列
FlexAlign.SpaceBetween 两端对齐 首个子组件在顶部,最后一个在底部,剩余空间平均分布在子项之间
FlexAlign.SpaceAround 环绕对齐 每个子组件两侧有相等的空间
FlexAlign.SpaceEvenly 均匀对齐 子组件之间的间距和两端间距相等
直观对比(垂直排列 3 个子组件):

Start:        Center:       End:          SpaceBetween:  SpaceAround:   SpaceEvenly:
┌────────┐    ┌────────┐    ┌────────┐    ┌────────┐     ┌────────┐     ┌────────┐
│  A     │    │        │    │        │    │  A     │     │        │     │        │
│        │    │        │    │        │    │        │     │   A    │     │   A    │
│  B     │    │  A     │    │        │    ├────────┤     │        │     │        │
│        │    │        │    │        │    │        │     ├────────┤     ├────────┤
│  C     │    │  B     │    │        │    │  B     │     │        │     │        │
│        │    │        │    │        │    │        │     │   B    │     │   B    │
└────────┘    │  C     │    │  A     │    ├────────┤     │        │     │        │
              │        │    │        │    │        │     ├────────┤     ├────────┤
              └────────┘    │  B     │    │  C     │     │        │     │        │
                            │        │    │        │     │   C    │     │   C    │
                            │  C     │    └────────┘     │        │     │        │
                            │        │                   └────────┘     └────────┘
                            └────────┘

3.6 alignItems —— 交叉轴对齐

alignItems 控制子组件在交叉轴(对于 FlexColumn 即水平方向)上的对齐方式。

使用 ItemAlign 枚举:

ItemAlign 值 效果 说明
ItemAlign.Auto 自动 默认值,相当于 Stretch
ItemAlign.Start 左对齐 子组件靠左排列
ItemAlign.Center 水平居中 子组件在交叉轴(水平)居中
ItemAlign.End 右对齐 子组件靠右排列
ItemAlign.Stretch 拉伸 子组件在交叉轴方向拉伸填满容器(在不设置宽度时生效)
ItemAlign.Baseline 基线对齐 子组件的文本基线对齐
3.7 alignSelf —— 子项独立对齐
alignSelf 允许单个子组件覆盖父容器通过 alignItems 设置的对齐方式。

Flex({
alignItems: ItemAlign.Center, // 父容器统一设为居中
}) {
Text(‘居中’).alignSelf(ItemAlign.Center) // 默认,继承父容器
Text(‘靠右’).alignSelf(ItemAlign.End) // 覆盖:单独靠右
Text(‘靠左’).alignSelf(ItemAlign.Start) // 覆盖:单独靠左
}
alignSelf 的优先级高于 alignItems,这在需要某些特殊子组件偏离整体对齐方式时非常有用。

3.8 flexWrap —— 换行控制
当 Flex 容器在一行/列中无法容纳所有子组件时,flexWrap 控制是否换行。

FlexWrap 值 效果
FlexWrap.NoWrap 不换行,子组件压缩以适应容器
FlexWrap.Wrap 换行,子组件保持原始尺寸换行排列
FlexWrap.WrapReverse 反向换行
在 Column 方向下,换行意味着子组件排满一列后,另起一列继续排列,类似于报纸的多栏布局。

四、完整示例代码逐段解析

下面我们对示例代码中的 5 个演示区域进行逐段解析,帮助读者深入理解每段代码的意图和效果。

4.1 基础 Column 排列(无弹性)
代码片段:

Flex({
direction: FlexDirection.Column, // 主轴设为垂直方向
justifyContent: FlexAlign.SpaceAround, // 主轴:首尾留空,均匀分布
alignItems: ItemAlign.Center, // 交叉轴:居中
}) {
Text(‘固定高度 (80px)’).width(‘80%’).height(80).backgroundColor(‘#FFB3BA’)
Text(‘固定高度 (60px)’).width(‘80%’).height(60).backgroundColor(‘#BAFFC9’)
Text(‘固定高度 (100px)’).width(‘80%’).height(100).backgroundColor(‘#BAE1FF’)
}
.width(‘100%’).height(300).padding(12)
布局要点:

这是最简单的 FlexColumn 用法,每个子项通过 height 指定固定高度。
justifyContent: FlexAlign.SpaceAround 让三个子项在 300px 容器中等距分布。
alignItems: ItemAlign.Center 让所有子项在水平方向居中。
三个子项宽度设为 ‘80%’,相对于父容器宽度的 80%,因此不会填满整个容器,居中效果可见。
效果预览:

三个彩色方块从上到下排列,水平居中,上下间距相等。浅粉色块高 80px,浅绿色块高 60px,浅蓝色块高 100px。

4.2 flexGrow 弹性扩张
在这里插入图片描述

代码片段:

Flex({
direction: FlexDirection.Column,
justifyContent: FlexAlign.Start,
alignItems: ItemAlign.Stretch, // 拉伸填满宽度
}) {
Text(‘flexGrow(1)’).flexGrow(1).height(50).backgroundColor(‘#FF9F9F’)
Text(‘flexGrow(2)’).flexGrow(2).height(50).backgroundColor(‘#9FB8FF’)
Text(‘flexGrow(0) → 保持 50px’).flexGrow(0).height(50).backgroundColor(‘#BCFF9F’)
}
.width(‘100%’).height(250).padding(12)
布局要点:

justifyContent: FlexAlign.Start:子项从顶部开始排列,剩余空间在底部。
alignItems: ItemAlign.Stretch:子项拉伸填满整个宽度(子项没有设置 width 时会自动拉伸)。
flexGrow(1) 和 flexGrow(2) 将按 1:2 的比例分配剩余空间。
flexGrow(0) 保持原始 50px 高度不变。
弹性计算演示:

容器可用高度:250 - 24(内边距) = 226px
三个子项原始高度:50 + 50 + 50 = 150px
剩余空间:226 - 150 = 76px

总权重:1 + 2 + 0 = 3
A (grow=1):50 + 76 × (1/3) ≈ 75px — 占 1 份
B (grow=2):50 + 76 × (2/3) ≈ 101px — 占 2 份,明显更高
C (grow=0):50px — 不扩张,保持不变
4.3 flexShrink 弹性收缩
代码片段:

Flex({
direction: FlexDirection.Column,
justifyContent: FlexAlign.Start,
alignItems: ItemAlign.Stretch,
}) {
Text(‘flexShrink(0) 不压缩’).flexShrink(0).height(120).backgroundColor(‘#FFD700’)
Text(‘flexShrink(1) 可压缩’).flexShrink(1).height(120).backgroundColor(‘#87CEEB’)
Text(‘flexShrink(2) 压缩×2’).flexShrink(2).height(120).backgroundColor(‘#98FB98’)
}
.width(‘100%’).height(200).padding(12)
布局要点:

容器高度 200px,内边距 24px,可用空间 176px。
三个子项原始高度之和 360px > 176px,触发收缩机制。
flexShrink(0):完全不压缩,保持 120px。
flexShrink(1):正常压缩(默认值)。
flexShrink(2):压缩速度是 flexShrink(1) 的两倍。
⚠️ 实际显示说明: 由于 flexShrink(2) 压缩量过大,可能导致子项高度被压缩到小于其文本内容所需的最小高度。此时 ArkTS 会以内容最小高度为下限,剩余压缩量由其他可压缩的子项分摊。这是符合预期的行为,与 CSS Flexbox 规范一致。

4.4 alignSelf 单独控制对齐
代码片段:

Flex({
direction: FlexDirection.Column,
justifyContent: FlexAlign.SpaceAround,
alignItems: ItemAlign.Center, // 父容器交叉轴:居中
}) {
Text(‘父→Center’)
.width(‘60%’).height(60).backgroundColor(‘#FFB3BA’)
.borderRadius(8)

Text(‘alignSelf=End’)
.alignSelf(ItemAlign.End) // 覆盖:单独靠右
.width(‘60%’).height(60).backgroundColor(‘#BAFFC9’)
.borderRadius(8)

Text(‘alignSelf=Start’)
.alignSelf(ItemAlign.Start) // 覆盖:单独靠左
.width(‘60%’).height(60).backgroundColor(‘#BAE1FF’)
.borderRadius(8)
}
.width(‘100%’).height(280).padding(12)
布局要点:

父容器设置 alignItems: ItemAlign.Center,理论上所有子项都应在水平方向居中。
但子项 2 使用 alignSelf(ItemAlign.End) 覆盖了父容器的设置,单独靠右对齐。
子项 3 使用 alignSelf(ItemAlign.Start) 覆盖,单独靠左对齐。
子项 1 没有设置 alignSelf,继承父容器的 Center,保持居中。
alignSelf 覆盖规则:

父容器 alignItems: Center
├── 子项 A:alignSelf 未设置 → 继承 Center(居中)
├── 子项 B:alignSelf(End) → 覆盖为 End(靠右)
└── 子项 C:alignSelf(Start) → 覆盖为 Start(靠左)
4.5 实战:垂直卡片列表
代码片段:

Flex({
direction: FlexDirection.Column,
justifyContent: FlexAlign.SpaceBetween,
alignItems: ItemAlign.Stretch,
}) {
Column() {
Text(‘📌 通知标题’).fontSize(16).fontWeight(FontWeight.Bold)
Text(‘这里是通知的详细内容…’).fontSize(14).fontColor(‘#666’)
}
.flexGrow(1)
.padding(16)
.backgroundColor(‘#FFFFFF’)
.borderRadius(12)
.shadow({ radius: 4, color: ‘#33000000’ })

// 另外两个类似的卡片…
}
.width(‘100%’).height(300).padding(12)
布局要点:

这是 FlexColumn 在实际项目中的典型应用场景:动态高度的卡片列表。
每个卡片使用 flexGrow(1),三个卡片等分剩余空间。
justifyContent: FlexAlign.SpaceBetween 确保卡片之间的间距均匀。
alignItems: ItemAlign.Stretch 让所有卡片宽度填满容器。
即使某个卡片的内容更多(高度变化),所有卡片也能自动调整为相同高度,视觉上非常整齐。
为什么不用 Column 组件?

Column 组件是 ArkTS 提供的简化垂直布局组件,它不具备弹性分配能力。如果使用 Column:

Column() {
// 卡片 1(固定高度)
// 卡片 2(固定高度)
// 卡片 3(固定高度)
}
每个卡片的高度必须固定或通过权重手动计算,无法自动等分剩余空间。而 Flex + flexGrow 可以优雅地解决这个问题。

五、实际应用场景

FlexColumn 布局在 HarmonyOS 应用开发中有着广泛的应用场景:

5.1 首页 Feed 流卡片
新闻类或社交类应用的首页通常由多个垂直排列的卡片组成,每个卡片包含标题、摘要、图片等信息。使用 FlexColumn 可以让所有卡片高度自动对齐:

Flex({ direction: FlexDirection.Column }) {
ForEach(this.newsList, (item: NewsItem) => {
NewsCard({ data: item })
.flexGrow(1) // 所有卡片等分高度
.margin({ bottom: 12 })
})
}
5.2 表单页面
表单通常由多个输入字段垂直堆叠而成。FlexColumn 可以保证表单区域均匀分布:

Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.SpaceBetween }) {
TextInput({ placeholder: ‘用户名’ }).flexGrow(1)
TextInput({ placeholder: ‘密码’ }).flexGrow(1)
TextInput({ placeholder: ‘确认密码’ }).flexGrow(1)
Button(‘提交’).flexGrow(0).height(48) // 按钮不伸缩
}
.height(300)
5.3 商品详情页
商品详情页的"信息-价格-操作按钮"垂直布局:

Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Stretch }) {
// 顶部:商品名称和描述(弹性占据中间空间)
Column() { /* 商品信息 */ }.flexGrow(1)

// 中间:价格区域(固定高度)
Row() { /* 价格标签 */ }.height(60)

// 底部:购买按钮(固定高度)
Button(‘立即购买’).height(48)
}
5.4 弹窗/对话框
自定义 Dialog 的内容区域:

Flex({ direction: FlexDirection.Column }) {
Text(‘提示’).fontSize(18).fontWeight(FontWeight.Bold)

// 内容区域弹性伸展
Column() { /* 弹窗内容 */ }.flexGrow(1)

// 底部按钮
Row() {
Button(‘取消’)
Button(‘确认’)
}
.flexGrow(0)
.height(44)
}
.height(280).padding(24)

六、FlexColumn 与其他布局方式对比

对比维度 FlexColumn Column Stack RelativeContainer
排列方向 垂直(可弹性) 垂直(不可弹性) 层叠(Z 轴) 锚点定位
弹性伸缩 ✅ flexGrow/flexShrink ❌ 不支持 ❌ 不支持 ❌ 不支持
对齐控制 主轴 + 交叉轴多维控制 简单水平对齐 单一对齐方式 锚点相对对齐
换行能力 ✅ flexWrap 支持 ❌ 不支持 ❌ 不支持 ❌ 不支持
子项顺序 ✅ 可调整 按代码顺序 按代码顺序 ✅ 可任意定位
适用场景 自适应列表、弹性卡片 简单垂直排列 悬浮层、重叠效果 复杂精确布局
选择建议:

需要弹性分配空间 → 用 FlexColumn
简单的固定高度垂直排列 → 用 Column
需要层叠效果(如悬浮按钮) → 用 Stack
需要精确的锚点定位 → 用 RelativeContainer

七、最佳实践与注意事项

7.1 常用配置组合模板
模板 1:等分高度列表

Flex({
direction: FlexDirection.Column,
justifyContent: FlexAlign.SpaceBetween,
alignItems: ItemAlign.Stretch,
}) {
// 所有子项设置 flexGrow(1)
}
模板 2:顶部固定 + 底部弹性

Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Stretch,
}) {
// 顶部区域(固定高度)
HeaderView().height(60)
// 中间内容(弹性填充)
ContentView().flexGrow(1)
// 底部栏(固定高度)
BottomBar().height(50).flexGrow(0)
}
模板 3:垂直居中容器

Flex({
direction: FlexDirection.Column,
justifyContent: FlexAlign.Center,
alignItems: ItemAlign.Center,
}) {
// 子组件在容器中垂直水平双居中
}
7.2 性能建议
避免深层嵌套:过多的 Flex 嵌套会降低布局性能。建议不超过 3-4 层嵌套。
合理使用 flexGrow:不要给所有子项都设置 flexGrow,只需要给需要弹性伸缩的项设置。
固定尺寸优先:对于不需要弹性的子项,使用固定尺寸(height)而不是 flexGrow(0)。虽然效果相同,但固定尺寸的布局计算更快。
懒加载长列表:当 FlexColumn 中的子项数量超过 20 个时,考虑使用 List 组件替代,List 支持视图回收,内存占用更低。
7.3 常见问题与解决方案
Q1:flexGrow 设置了但没有效果?

检查以下几点:

确认 Flex 容器设置了固定高度(或百分比高度)。
确认所有子项的原始高度之和小于容器高度。
检查是否同时设置了 flexShrink,收缩可能抵消扩张。
Q2:flexShrink 导致子项消失?

当子项的 flexShrink 值过大,压缩后高度可能变为负数。ArkTS 会以子项的内容最小高度为下限。解决方案是合理设置 flexShrink 值,或对重要子项设置 flexShrink(0)。

Q3:alignItems: Stretch 为什么没有拉伸?

Stretch 拉伸仅在子项没有显式设置宽度时生效。如果子项通过 width() 设置了固定宽度,则 Stretch 不会覆盖它。

Q4:FlexColumn 和 Column 组件有什么区别?

Column 是 ArkTS 对 Flex + Column 的语法糖封装,提供了更简洁的 API。但当需要 flexGrow、flexShrink、flexBasis 等弹性特性时,必须使用完整的 Flex 组件。

八、示例代码完整运行说明

8.1 运行环境要求
要运行本文配套的示例应用,需要满足以下环境条件:

环境项 要求
操作系统 Windows 10/11、macOS 13+ 或 Ubuntu 22.04+
DevEco Studio 5.0.3 Release 及以上版本
HarmonyOS SDK API 12 Release 及以上版本
运行设备 HarmonyOS NEXT 模拟器或真机(API 12+)
项目类型 Stage 模型 + ArkTS 声明式开发范式
8.2 运行步骤
第一步:导入项目

打开 DevEco Studio,选择 “Open” 或 “导入”,定位到项目目录 D:\HarmonyOS-Life\Demo0610,等待 Gradle 同步完成。

第二步:配置签名

如果遇到 “Will skip sign” 的警告,需要配置自动签名:

在 DevEco Studio 中点击 File → Project Structure → Project → Signing Configs
勾选 “Automatically generate signing”
点击 “OK” 保存配置
或者手动配置 build-profile.json5 中的 signingConfigs 字段。

第三步:选择设备

如果使用模拟器:点击设备列表 → “Device Manager” → 启动一个 API 12+ 的模拟器
如果使用真机:通过 USB 连接已开启开发者模式的 HarmonyOS NEXT 设备
第四步:运行应用

点击工具栏的 “Run” 按钮(绿色三角形)或使用快捷键 Shift + F10,等待编译和部署完成。

8.3 预览效果说明
应用启动后将直接展示 FlexColumn 弹性布局的 5 个演示区域。页面可上下滚动,每个区域顶部有标题说明当前演示的技术要点。各区域以灰色背景的卡片形式呈现,其中的彩色方块代表不同的子组件,通过不同的颜色和尺寸直观反映弹性布局的效果。

九、深入理解弹性布局的数学原理

9.1 flexGrow 的数学计算模型
flexGrow 的计算本质上是按比例分配剩余空间的过程。其数学模型可以表示为:

子项最终尺寸 = 子项原始尺寸 + 剩余空间 × (子项 flexGrow / 所有 flexGrow 之和)
其中:

剩余空间 = 容器主轴尺寸 - 所有子项原始尺寸之和 - 间距 - 内边距
只有 flexGrow > 0 的子项才能分配到剩余空间
所有 flexGrow = 0 的子项保持原始尺寸
复杂场景示例:

假设有一个 500px 高的 FlexColumn 容器,包含 4 个子项,原始高度分别为 60px、80px、100px、40px,内边距 20px(上下各 10px)。

可用高度 = 500 - 20 = 480px
子项原始尺寸总和 = 60 + 80 + 100 + 40 = 280px
剩余空间 = 480 - 280 = 200px

flexGrow 设置:0, 1, 2, 1
总权重 = 0 + 1 + 2 + 1 = 4

计算结果:
子项 A(grow=0):60 + 200 × (0/4) = 60px
子项 B(grow=1):80 + 200 × (1/4) = 130px
子项 C(grow=2):100 + 200 × (2/4) = 200px
子项 D(grow=1):40 + 200 × (1/4) = 90px

验证总和:60 + 130 + 200 + 90 = 480px ✓
9.2 flexShrink 的数学计算模型
flexShrink 的计算基于加权压缩比例,其数学模型为:

子项压缩量 = 溢出空间 × (子项 flexShrink × 子项原始尺寸) / Σ(每个子项 flexShrink × 其原始尺寸)

子项最终尺寸 = 子项原始尺寸 - 子项压缩量
其中:

溢出空间 = 子项原始尺寸之和 - 容器可用主轴尺寸(正值)
压缩量正比于 flexShrink 值与原始尺寸的乘积
flexShrink = 0 的子项不参与压缩
复杂场景示例:

容器可用高度 = 200px(含 20px 内边距,实际可用 180px)
子项原始尺寸:100px, 120px, 80px, 60px
flexShrink 设置:0, 1, 2, 1

原始总和 = 100 + 120 + 80 + 60 = 360px
溢出空间 = 360 - 180 = 180px

加权因子计算:
子项 A:0 × 100 = 0
子项 B:1 × 120 = 120
子项 C:2 × 80 = 160
子项 D:1 × 60 = 60
总加权值 = 0 + 120 + 160 + 60 = 340

压缩量计算:
子项 A(shrink=0):180 × (0/340) = 0px → 不压缩,保持 100px
子项 B(shrink=1):180 × (120/340) ≈ 63.5px → 高度变为 120 - 63.5 = 56.5px
子项 C(shrink=2):180 × (160/340) ≈ 84.7px → 高度变为 80 - 84.7 = -4.7px → 取内容最小高度
子项 D(shrink=1):180 × (60/340) ≈ 31.8px → 高度变为 60 - 31.8 = 28.2px
9.3 联合使用 flexGrow 和 flexShrink
在同一个 Flex 容器中,flexGrow 和 flexShrink 可以同时使用,但不会同时生效:

空间充足时 → flexGrow 生效,flexShrink 被忽略
空间不足时 → flexShrink 生效,flexGrow 被忽略
空间恰好相等时 → 两者都不生效,子项保持原始尺寸
这种设计确保了布局行为的确定性和可预测性。

十、FlexColumn 与其他编程框架的对比

10.1 与 CSS Flexbox 对比
特性 ArkTS Flex CSS Flexbox
设置方向 direction: FlexDirection.Column flex-direction: column
弹性扩张 .flexGrow(n) 链式调用 flex-grow: n 属性声明
弹性收缩 .flexShrink(n) 链式调用 flex-shrink: n 属性声明
基准尺寸 .flexBasis(n) 链式调用 flex-basis: n 属性声明
主轴对齐 justifyContent: FlexAlign.* justify-content: *
交叉轴对齐 alignItems: ItemAlign.* align-items: *
子项自对齐 .alignSelf(ItemAlign.*) align-self: *
换行 wrap: FlexWrap.Wrap flex-wrap: wrap
简写属性 不支持 flex 简写 flex: grow shrink basis
10.2 与 Flutter 对比
对应关系 ArkTS Flutter
弹性容器 Flex({ direction: FlexDirection.Column }) Flex(direction: Axis.vertical)
简化版 无(Column 无弹性) Column
弹性子项 .flexGrow(1) Expanded(flex: 1)
弹性收缩 .flexShrink(1) Flexible(fit: FlexFit.loose)
主轴对齐 justifyContent: FlexAlign.SpaceBetween mainAxisAlignment: MainAxisAlignment.spaceBetween
交叉轴对齐 alignItems: ItemAlign.Center crossAxisAlignment: CrossAxisAlignment.center
10.3 与 Android Jetpack Compose 对比
对应关系 ArkTS Jetpack Compose
弹性容器 Flex({ direction: FlexDirection.Column }) Column
弹性权重 .flexGrow(1f) Modifier.weight(1f)
主轴对齐 justifyContent: FlexAlign.Center verticalArrangement: Arrangement.Center
交叉轴对齐 alignItems: ItemAlign.Center horizontalAlignment: Alignment.CenterHorizontally

十一、常见面试题与解答

11.1 基础题
问:flexGrow 和 flexShrink 的区别是什么?

答:flexGrow 控制当容器有剩余空间时子项的扩张比例,flexShrink 控制当容器空间不足时子项的压缩比例。flexGrow 的默认值是 0(不扩张),flexShrink 的默认值是 1(可压缩)。两者在同一容器中互斥生效——空间充足时用 flexGrow,空间不足时用 flexShrink。

问:alignItems 和 alignSelf 有什么区别?

答:alignItems 是设置在 Flex 父容器上的属性,作用于所有子项,控制它们在交叉轴方向的对齐方式。alignSelf 是设置在单个子项上的属性,只作用于该子项,可以覆盖父容器的 alignItems 设置。alignSelf 的优先级高于 alignItems。

11.2 进阶题
问:在 FlexColumn 中,如果同时设置了子项的 height 和 flexGrow,哪种属性最终决定子项高度?

答:当设置了 flexGrow 且容器有剩余空间时,弹性扩张机制会覆盖 height 设置的原始高度。具体计算是:子项的原始高度(即 height 或 flexBasis 设置的值)只是弹性计算的基准,最终高度 = 原始高度 + 分配到的剩余空间。如果 flexGrow = 0,则保持原始高度。

问:SpaceBetween 和 SpaceAround 有什么区别?

答:SpaceBetween 将第一个子项放在主轴起点,最后一个子项放在主轴终点,剩余空间平均分配在子项之间。SpaceAround 在每个子项的两侧分配相等的空间,因此第一个子项的上方和最后一个子项的下方也有一半的间距。SpaceBetween 的两端没有间距,而 SpaceAround 的两端有间距。

11.3 实战题
问:如何实现一个"顶部固定 + 中间弹性滚动 + 底部固定"的典型页面布局?

答:使用两层 FlexColumn:

Flex({ direction: FlexDirection.Column, height: ‘100%’ }) {
// 顶部:固定高度标题栏
Row() { /* 标题 */ }.height(56)

// 中间:弹性区域,可滚动
Scroll() {
Flex({ direction: FlexDirection.Column }) {
/* 内容列表 */
}
}
.flexGrow(1) // 弹性填充中间区域

// 底部:固定高度操作栏
Row() { /* 底部按钮 */ }.height(48)
}
这种布局模式在 HarmonyOS 应用中非常常见,称为"头-体-脚"三段式布局,是构建页面骨架的标准方案。

十二、总结

FlexColumn 弹性布局(即 Flex + direction: FlexDirection.Column)是 HarmonyOS NEXT 应用开发中最核心、最灵活的布局方式之一。通过本文的详细介绍,我们深入理解了以下关键概念:

核心技术回顾
主轴与交叉轴:Column 方向下,主轴为垂直方向,交叉轴为水平方向。
flexGrow:子组件按比例分配剩余空间,值越大占的比例越多。
flexShrink:空间不足时子组件按比例压缩,值越大压缩越多。
justifyContent:控制主轴(垂直)对齐方式,支持 Start、Center、End、SpaceBetween、SpaceAround、SpaceEvenly。
alignItems:控制交叉轴(水平)对齐方式,支持 Start、Center、End、Stretch、Baseline。
alignSelf:允许单个子组件覆盖父容器的 alignItems 设置。
学习路线建议
入门:掌握 Flex({ direction: FlexDirection.Column }) 的基本用法。
进阶:理解 flexGrow 和 flexShrink 的弹性计算原理。
实战:结合 ForEach 和自定义组件,构建动态卡片列表。
精通:将 FlexColumn 与其他布局(Stack、RelativeContainer)组合使用。
FlexColumn 布局是构建响应式、自适应鸿蒙应用的基石。掌握了它,你就掌握了 HarmonyOS NEXT 应用开发中 80% 以上的布局需求。

十三、参考资料

HarmonyOS NEXT 开发者文档 - Flex 组件
HarmonyOS NEXT 开发者文档 - 布局概述
ArkTS 语言编程规范
CSS Flexible Box Layout Module (W3C) — Flex 布局的 W3C 标准规范,ArkTS 的 Flex 组件与其保持高度一致
本文配套示例代码位于同目录下的 entry/src/main/ets/pages/Index.ets,用 DevEco Studio 打开项目并运行即可查看各布局效果。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐