鸿蒙原生 ArkTS 布局之道:Flex 实现垂直居中 —— 最简洁的居中方案


在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

一、引言:一个老问题的新解法

在前端与客户端开发中,“将一个元素在父容器内水平和垂直双向居中"堪称最经典也最折磨人的布局需求之一。在 Web 端,我们经历过 margin: 0 auto 配合 line-height 的奇技淫巧,见证了 display: flex 配合 justify-content: centeralign-items: center 如何用两行代码终结了这场长达二十年的"居中战争”。如今,在鸿蒙原生 ArkTS 开发中,历史惊人相似地重演了。

HarmonyOS NEXT API 24 作为鸿蒙生态的最新里程碑,对 ArkUI 声明式框架进行了大量优化。其中,Flex 弹性布局接口迎来了重要更新:justifyContentalignItems 被正式纳入 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 推荐优先使用 构造参数式,理由有三:

  1. 编译期类型检查更严格FlexOptions 接口对每个字段有精确的类型约束,IDE 可以在编码阶段捕获拼写错误或类型不匹配。
  2. 性能微优化:构造参数在组件创建阶段一次性解析,避免了链式调用可能触发的多次属性合并开销(尽管在实际场景中差异可以忽略不计)。
  3. 语义自文档化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 }) 的联合效果可以用一句话概括:“将唯一的子元素(或所有子元素形成的整体)同时置于主轴的中央和交叉轴的中央。”

其底层逻辑如下:

  1. Flex 首先沿主轴(水平方向)分配空间,Center 值将子元素推至中间位置;
  2. 然后沿交叉轴(垂直方向)分配空间,Center 值再次将子元素推至中间位置;
  3. 两次定位叠加的结果就是子元素被精确地固定在父容器的几何中心。

这种逻辑的优雅之处在于它是 声明式 的——你只需要告诉框架"我要居中",而不需要关心父容器尺寸变化、子元素尺寸变化等动态因素。布局引擎会自动响应所有变化。


四、对比分析:为什么 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%')

缺点

  • 每行代码都需要同时指定 centermiddle,否则无法实现双向居中
  • 需要理解 __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%')

优点

  • 单容器无嵌套,节点树最扁平
  • 一行声明完成双向居中,代码量最少
  • 语义自明:看到 justifyContentalignItems 就知道布局意图是居中
  • 子元素数量无限制:一个子元素居中,多个子元素也居中(作为整体居中)
  • 自适应:父容器尺寸变化时自动重新计算,无需任何手动干预
  • 与 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 布局方面有若干重要变化:

  1. FlexOptions 构造参数成为一等公民:此前 justifyContentalignItems 仅能通过链式方法设置,API 24 强化了构造参数形式并推荐为首选写法。
  2. FlexAlign 枚举值精简与规范化:去除了历史遗留的冗余别名,所有枚举值统一采用帕斯卡命名(如 FlexAlign.Center),不再支持旧版的大小写变体。
  3. ItemAlign 枚举新增 Baseline 值:为文本混排场景提供了精确的对齐控制,此前该能力仅存在于 RowColumn 组件中。
  4. 编译期验证增强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 容器的 widthheight 都有明确的数值或百分比。

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 核心要点回顾

  1. Flex 是 HarmonyOS NEXT 中最推荐的全屏居中容器,相比 RelativeContainerStack,它在代码简洁性、语义清晰度和动态适应能力上全面胜出。
  2. justifyContent 控制主轴居中,alignItems 控制交叉轴居中,两者组合实现双向居中,缺一不可。
  3. API 24 推荐使用构造参数式 Flex({ justifyContent: FlexAlign.Center, alignItems: ItemAlign.Center }),类型安全且语义自明。
  4. 务必为 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)中直接打开项目并运行查看效果。

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐