【共创季稿事节】鸿蒙原生 ArkTS 布局之道:Flex 实现垂直居中 —— 最简洁的居中方案
鸿蒙原生 ArkTS 布局之道:Flex 实现垂直居中 —— 最简洁的居中方案



一、引言:一个老问题的新解法
在前端与客户端开发中,“将一个元素在父容器内水平和垂直双向居中"堪称最经典也最折磨人的布局需求之一。在 Web 端,我们经历过 margin: 0 auto 配合 line-height 的奇技淫巧,见证了 display: flex 配合 justify-content: center 与 align-items: center 如何用两行代码终结了这场长达二十年的"居中战争”。如今,在鸿蒙原生 ArkTS 开发中,历史惊人相似地重演了。
HarmonyOS NEXT API 24 作为鸿蒙生态的最新里程碑,对 ArkUI 声明式框架进行了大量优化。其中,Flex 弹性布局接口迎来了重要更新:justifyContent 与 alignItems 被正式纳入 FlexOptions 构造参数,使开发者可以在创建 Flex 容器的瞬间一次性声明主次轴的排列策略,无需在链式调用中二次追加。
本文将以一个完整可运行的 .ets 文件为例,深度拆解这一"一行代码 + 一行 alignItems 实现完美居中"的极简方案,并揭示其背后的布局原理、API 演进脉络以及工程实践中的最佳抉择。
二、代码先行:完整示例速览
以下代码基于 API 24 编写,已通过 hvigorw assembleHap 编译验证。删除原有 pages/Index.ets 内容并替换为本代码,即可在模拟器或真机上看到效果。
/**
* 示例:Flex 实现垂直居中 —— 最简洁的居中方案
*
* 核心技术:
* - Flex:弹性布局容器,默认主轴为水平方向(row)
* - justifyContent(FlexAlign.Center):主轴(水平)居中
* - alignItems(ItemAlign.Center):交叉轴(垂直)居中
*
* 一句话总结:
* 只需在 Flex 容器上设置 justifyContent 与 alignItems 两个属性,
* 即可实现子组件的水平和垂直双向完美居中,无需计算偏移量。
*/
@Entry
@Component
struct Index {
build() {
// 【核心】Flex 弹性容器,居中参数通过 FlexOptions 构造器传入
Flex({
justifyContent: FlexAlign.Center,
alignItems: ItemAlign.Center
}) {
// 被居中的内容卡片
Column() {
Text('\u{1F3AF}') // 靶心 Emoji
.fontSize(48)
Text('完美居中')
.fontSize(24)
.fontWeight(FontWeight.Bold)
.fontColor('#FF6C6C6C')
.margin({ top: 12 })
Text('Flex + justifyContent + alignItems')
.fontSize(14)
.fontColor('#FF999999')
.margin({ top: 8 })
Divider()
.width('60%')
.color('#FFE0E0E0')
.margin({ top: 16, bottom: 16 })
Text('\u300C\u4E00\u884C\u4EE3\u7801 + alignItems \u5B9E\u73B0\u5B8C\u7F8E\u5C45\u4E2D\u300D')
.fontSize(13)
.fontColor('#FF333333')
.textAlign(TextAlign.Center)
.lineHeight(20)
Text('\u2022 justifyContent(FlexAlign.Center) \u2192 \u6C34\u5E73\u5C45\u4E2D')
.fontSize(12)
.fontColor('#FF666666')
.margin({ top: 8 })
Text('\u2022 alignItems(ItemAlign.Center) \u2192 \u5782\u76F4\u5C45\u4E2D')
.fontSize(12)
.fontColor('#FF666666')
.margin({ top: 4 })
}
.width('85%')
.padding(24)
.borderRadius(16)
.backgroundColor('#FFF8F8FF')
.shadow({
radius: 12,
color: '#33000000',
offsetX: 0,
offsetY: 4
})
.alignItems(HorizontalAlign.Center)
}
.width('100%')
.height('100%')
.backgroundColor('#FFE8F0FE')
}
}
运行效果:一张柔蓝色背景铺满屏幕,一张带有柔和阴影的白色卡片精准悬浮在屏幕正中央,卡片内以优雅的排版展示技术要点说明。
三、逐行拆解:FlexOptions 构造参数详解
3.1 Flex 构造器签名变迁
在 API 24 中,Flex 组件支持两种使用形态:
| 形态 | 写法 | 适用场景 |
|---|---|---|
| 构造参数式 | Flex({ justifyContent, alignItems, ... }) { content } |
布局参数固定不变,声明即定型 |
| 链式属性式 | Flex() { content }.justifyContent(...).alignItems(...) |
需按条件动态切换布局参数 |
API 24 推荐优先使用 构造参数式,理由有三:
- 编译期类型检查更严格:
FlexOptions接口对每个字段有精确的类型约束,IDE 可以在编码阶段捕获拼写错误或类型不匹配。 - 性能微优化:构造参数在组件创建阶段一次性解析,避免了链式调用可能触发的多次属性合并开销(尽管在实际场景中差异可以忽略不计)。
- 语义自文档化:
Flex({ justifyContent: ..., alignItems: ... })读起来就像是"创建一个弹性容器,其主轴居中对齐、交叉轴居中对齐",布局意图一目了然。
3.2 justifyContent 的完整枚举
justifyContent 接受 FlexAlign 枚举,该枚举在 API 24 中定义了 六种取值:
| 枚举值 | 行为描述 |
|---|---|
FlexAlign.Start |
子元素紧贴主轴起点排列(默认值) |
FlexAlign.Center |
子元素在主轴上居中对齐 |
FlexAlign.End |
子元素紧贴主轴终点排列 |
FlexAlign.SpaceBetween |
首尾元素贴边,剩余空间均匀分布在元素之间 |
FlexAlign.SpaceAround |
每个元素两侧空间相等,首尾空间为间隔的一半 |
FlexAlign.SpaceEvenly |
所有元素之间及首尾的空间完全相等 |
当 flexDirection 为默认值 FlexDirection.Row 时,主轴为 水平方向,因此 FlexAlign.Center 实现了水平居中。
3.3 alignItems 的完整枚举
alignItems 接受 ItemAlign 枚举,在 API 24 中定义了 五种取值:
| 枚举值 | 行为描述 |
|---|---|
ItemAlign.Start |
子元素紧贴交叉轴起点(默认值) |
ItemAlign.Center |
子元素在交叉轴上居中对齐 |
ItemAlign.End |
子元素紧贴交叉轴终点 |
ItemAlign.Stretch |
子元素沿交叉轴方向拉伸至容器尺寸(要求子元素未显式设置该维度尺寸) |
ItemAlign.Baseline |
子元素按文本基线对齐 |
当 Flex 默认水平排列时,交叉轴为 垂直方向,因此 ItemAlign.Center 实现了垂直居中。
3.4 两属性协同的几何逻辑
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) 的联合效果可以用一句话概括:“将唯一的子元素(或所有子元素形成的整体)同时置于主轴的中央和交叉轴的中央。”
其底层逻辑如下:
Flex首先沿主轴(水平方向)分配空间,Center值将子元素推至中间位置;- 然后沿交叉轴(垂直方向)分配空间,
Center值再次将子元素推至中间位置; - 两次定位叠加的结果就是子元素被精确地固定在父容器的几何中心。
这种逻辑的优雅之处在于它是 声明式 的——你只需要告诉框架"我要居中",而不需要关心父容器尺寸变化、子元素尺寸变化等动态因素。布局引擎会自动响应所有变化。
四、对比分析:为什么 Flex 是最简洁的居中方案?
在 ArkUI 框架中,实现居中远不止一种方式。让我们逐一对比,看看为什么 Flex 方案能从众多候选中脱颖而出。
4.1 方案一:RelativeContainer + alignRules
RelativeContainer() {
Text('居中').alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
}
.height('100%').width('100%')
缺点:
- 每行代码都需要同时指定
center和middle,否则无法实现双向居中 - 需要理解
__container__锚点语义和alignRules的复杂语法 - 当子元素数量增多时,每个子元素都需要独立的
alignRules声明 - 代码冗长,嵌套层级深,不易阅读维护
4.2 方案二:Stack + position
Stack() {
Text('居中')
.position({ x: '50%', y: '50%' })
.translate({ x: '-50%', y: '-50%' })
}
.height('100%').width('100%')
缺点:
- 需要手动计算偏移量,且使用百分比偏移时依赖子元素自身的尺寸
position+translate组合写起来不够直观,调试时需要同时考虑两个属性- 不适合动态尺寸场景,子元素尺寸变化后需重新计算偏移
4.3 方案三:Column + Row 嵌套
Column() {
Row() {
Text('居中')
}
.width('100%')
.justifyContent(FlexAlign.Center)
}
.height('100%')
.width('100%')
.justifyContent(FlexAlign.Center)
缺点:
- 引入了一层多余的
Row容器,增加了虚拟节点的数量 - 代码结构冗长,明明是居中一个元素却需要两层容器嵌套
- 布局语义模糊,阅读代码时需要从两个方向分别理解
4.4 方案四:Flex 构造参数式(本文推荐)
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Text('居中')
}
.height('100%').width('100%')
优点:
- 单容器无嵌套,节点树最扁平
- 一行声明完成双向居中,代码量最少
- 语义自明:看到
justifyContent和alignItems就知道布局意图是居中 - 子元素数量无限制:一个子元素居中,多个子元素也居中(作为整体居中)
- 自适应:父容器尺寸变化时自动重新计算,无需任何手动干预
- 与 Web Flexbox 概念对标,降低 Web 开发者迁移成本
4.5 综合评分表
| 维度 | RelativeContainer | Stack + 偏移 | Column+Row 嵌套 | Flex(推荐) |
|---|---|---|---|---|
| 代码行数 | 7 ~ 9 | 5 ~ 6 | 6 ~ 7 | 3 ~ 4 |
| 容器嵌套层数 | 1 | 1 | 2 | 1 |
| 手动偏移计算 | 否 | 是 | 否 | 否 |
| 语义清晰度 | ★★★ | ★★ | ★★★ | ★★★★★ |
| 动态尺寸适应 | ★★★★ | ★★ | ★★★★ | ★★★★★ |
| Web 开发者友好 | ★★ | ★ | ★★★ | ★★★★★ |
五、API 24 新增特性与最佳实践
5.1 与之前 API 版本的关键差异
API 24(SDK 7.0.0)相比 API 12~23 在 Flex 布局方面有若干重要变化:
- FlexOptions 构造参数成为一等公民:此前
justifyContent和alignItems仅能通过链式方法设置,API 24 强化了构造参数形式并推荐为首选写法。 - FlexAlign 枚举值精简与规范化:去除了历史遗留的冗余别名,所有枚举值统一采用帕斯卡命名(如
FlexAlign.Center),不再支持旧版的大小写变体。 - ItemAlign 枚举新增 Baseline 值:为文本混排场景提供了精确的对齐控制,此前该能力仅存在于
Row和Column组件中。 - 编译期验证增强:
FlexOptions中的误写字段(如justifyContent: 'center'字符串而非FlexAlign.Center枚举)会直接报编译错误,而非运行时静默忽略。
5.2 常见踩坑与避坑指南
坑一:误以为 justifyContent 可以独立决定垂直居中
许多从 CSS 背景迁移过来的开发者会习惯性地认为 justifyContent 就是"居中"的全部。在默认 FlexDirection.Row 下,justifyContent 仅控制水平方向。要实现垂直居中,必须同时设置 alignItems: ItemAlign.Center。
正确做法:
// 双向居中,二者缺一不可
Flex({
justifyContent: FlexAlign.Center, // 水平
alignItems: ItemAlign.Center // 垂直
})
坑二:在 Column 子节点内部误用 alignItems
注意卡片内部的 .alignItems(HorizontalAlign.Center) 与卡片外部 Flex 上的 .alignItems(ItemAlign.Center) 是两类不同的 API:
Flex().alignItems(ItemAlign.Center)—— 作用于 flex 容器,控制所有 flex 子项在交叉轴上的对齐方式Column().alignItems(HorizontalAlign.Center)—— 作用于 Column 容器,控制其子项在水平方向上的对齐方式
两者名称相同但类型不同,使用时务必匹配正确的容器类型和枚举类型,否则编译会报错。
坑三:Flex 容器高度未设置导致视觉上未居中
如果父容器没有显式设置高度(或 height('100%')),而它的父级也没有固定高度,那么 Flex 容器自身的尺寸取决于子元素,此时 alignItems: ItemAlign.Center 的效果将不可见——因为交叉轴的实际可用空间等于子元素高度,无所谓"居中"可言。
黄金法则:在使用 Flex 进行居中布局时,务必确保 Flex 容器的 width 和 height 都有明确的数值或百分比。
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center })
.width('100%') // 必须有
.height('100%') // 必须有
5.3 进阶:多子元素居中与间距控制
本示例中 Flex 只有一个子元素(Column 卡片),但 Flex 的居中能力在多个子元素场景下同样强大:
Flex({
justifyContent: FlexAlign.SpaceEvenly,
alignItems: ItemAlign.Center
}) {
Button('左').width(80)
Button('中').width(80)
Button('右').width(80)
}
.width('100%').height(200)
上述代码实现了三个按钮水平均匀分布且垂直居中的效果,仅靠构造参数中的两个属性就完成了布局,不需要任何额外的容器嵌套。
如果需要子元素换行排列,可以额外指定 wrap: FlexWrap.Wrap:
Flex({
justifyContent: FlexAlign.Center,
alignItems: ItemAlign.Center,
wrap: FlexWrap.Wrap
}) {
// 动态生成的多个子组件
}
在 API 24 中,FlexOptions 支持的全部字段包括:
| 字段名 | 类型 | 说明 |
|---|---|---|
direction |
FlexDirection |
主轴方向(Row / Column / RowReverse / ColumnReverse) |
wrap |
FlexWrap |
是否换行(NoWrap / Wrap / WrapReverse) |
justifyContent |
FlexAlign |
主轴对齐方式 |
alignItems |
ItemAlign |
交叉轴对齐方式 |
alignContent |
FlexAlign |
多行时的行间对齐方式(仅在 wrap 启用时有效) |
六、工程实战:在真实项目中应用 Flex 居中
6.1 登录页 Logo + 表单居中
@Entry
@Component
struct LoginPage {
build() {
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Column() {
Image($r('app.media.logo')).width(120).height(120)
TextInput({ placeholder: '请输入账号' }).margin({ top: 24 })
TextInput({ placeholder: '请输入密码' }).margin({ top: 12 })
Button('登录').width('100%').margin({ top: 24 })
}
.width('85%')
}
.width('100%').height('100%')
.backgroundColor('#FFFFFF')
}
}
6.2 加载中状态全屏居中
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Column() {
LoadingProgress().width(48).height(48)
Text('加载中...')
.fontSize(14)
.fontColor('#FF666666')
.margin({ top: 16 })
}
}
.width('100%').height('100%')
.backgroundColor('#80FFFFFF')
6.3 空状态提示居中
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }) {
Column() {
Image($r('app.media.empty_icon')).width(80).height(80)
Text('暂无数据').fontSize(16).fontColor('#FF999999').margin({ top: 16 })
}
}
.width('100%').height('100%')
以上三个场景的共同模式都是:外层 Flex 负责双向居中,内层 Column 负责垂直排列内容。这种"Flex + Column"的组合是 ArkTS 布局中最常用、最稳定的居中范式。
七、总结与展望
7.1 核心要点回顾
- Flex 是 HarmonyOS NEXT 中最推荐的全屏居中容器,相比
RelativeContainer和Stack,它在代码简洁性、语义清晰度和动态适应能力上全面胜出。 justifyContent控制主轴居中,alignItems控制交叉轴居中,两者组合实现双向居中,缺一不可。- API 24 推荐使用构造参数式
Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }),类型安全且语义自明。 - 务必为 Flex 容器设置明确的宽高,否则居中无法生效。
7.2 从 API 24 展望未来
随着鸿蒙生态的持续演进,ArkUI 布局方案正在向更声明化、更类型安全的方向发展。API 24 对 FlexOptions 构造参数的强化只是一个开始。可以预见,未来的 API 版本可能会:
- 引入更丰富的布局组合约束,减少显式容器嵌套;
- 增强编译期布局语义检查,在编码阶段发现潜在的面板溢出或重叠问题;
- 与
@Builder、@BuilderParam等组件化方案更深度的融合,使布局代码的复用达到新的高度。
作为开发者,掌握 Flex 布局——这个既古老又现代的布局模型——将为你在鸿蒙世界中的 UI 开发之路打下最坚实的基础。
附录:API 24 下完整项目结构参考
app6201/
├── entry/
│ ├── src/main/ets/
│ │ ├── entryability/EntryAbility.ets
│ │ └── pages/
│ │ └── Index.ets ← 本文所有代码存放位置
│ ├── src/main/resources/
│ │ └── base/profile/
│ │ └── main_pages.json ← 路由配置,指向 pages/Index
│ └── build-profile.json5
├── build-profile.json5 ← compatibleSdkVersion: "7.0.0(24)"
├── hvigor/hvigor-config.json5
└── oh-package.json5
本文配套示例代码已通过
hvigorw --mode module -p module=entry -p product=default assembleHap编译验证,你可以在 DevEco Studio NEXT(API 24)中直接打开项目并运行查看效果。
更多推荐




所有评论(0)