这里写自定义目录标题

鸿蒙原生 ArkTS 布局方式深度解析:Column + alignItems(Start) 垂直排列

在这里插入图片描述

SDK 版本:HarmonyOS NEXT 6.1.1(API 24)
开发语言:ArkTS(声明式 UI)
项目模式:Stage 模型
适用场景:纵向列表、表单、信息流页面、个人资料页


目录

  1. 开篇:HarmonyOS NEXT 与 ArkTS 声明式 UI
  2. 项目结构概览
  3. Column 容器布局原理
  4. ColumnStart 布局详解(核心)
  5. alignItems 三种取值效果对比
  6. justifyContent 垂直对齐控制
  7. 实战场景一:信息流列表
  8. 实战场景二:表单页面
  9. 实战场景三:番茄钟应用
  10. @Builder 装饰器:复用布局组件
  11. 路由与页面导航
  12. 布局要点总结
  13. 常见问题与调试技巧
  14. 结语

1. 开篇:HarmonyOS NEXT 与 ArkTS 声明式 UI

HarmonyOS NEXT 是华为推出的全栈自研操作系统,从内核到应用框架完全去除了 Android 代码,采用自研的鸿蒙内核。HarmonyOS NEXT 6.1.1(API 24)是当前最新的稳定版本,为开发者提供了完善的 ArkTS 声明式 UI 开发体系。

ArkTS(Ark TypeScript)是鸿蒙原生应用开发语言,基于 TypeScript 语法扩展,加入了响应式状态管理、装饰器、声明式 UI 等特性。它与 SwiftUI(iOS)、Jetpack Compose(Android)的理念类似,但在布局系统上有着自己独特的设计。

在 ArkTS 中,布局的核心思想是 「容器 + 子组件 + 对齐方式」。开发者通过组合不同的容器组件(Column、Row、Flex、Stack、Grid 等)和对齐属性,可以构建任意复杂的 UI 界面。

本文将重点讲解 Column 容器 + alignItems(ItemAlign.Start) + justifyContent(FlexAlign.Start) 这一经典组合,即 ColumnStart 垂直排列布局,并通过完整的可运行代码示例,帮助读者深入理解其原理与应用。


2. 项目结构概览

在开始深入布局细节之前,我们先来看看整个项目的文件结构:

MyApplication4/
├── AppScope/
│   └── app.json5                      # 应用全局配置
├── entry/
│   ├── build-profile.json5            # 模块构建配置
│   ├── hvigorfile.ts                  # 构建脚本
│   ├── oh-package.json5               # 依赖管理
│   └── src/main/
│       ├── ets/
│       │   ├── entryability/
│       │   │   └── EntryAbility.ets   # Ability 生命周期管理
│       │   ├── entrybackupability/
│       │   │   └── EntryBackupAbility.ets
│       │   └── pages/
│       │       ├── Index.ets          # 首页 - 番茄钟
│       │       └── ColumnStartDemo.ets # 布局演示页
│       └── resources/
│           ├── base/
│           │   ├── element/           # 颜色、字重等资源
│           │   ├── media/             # 图片资源
│           │   └── profile/
│           │       └── main_pages.json # 页面路由注册
│           └── dark/
│               └── element/color.json # 深色模式颜色
├── build-profile.json5                # 工程级构建配置
└── hvigorfile.ts

2.1 页面路由注册

每个页面必须在 main_pages.json 中注册才能被 router.pushUrl() 跳转访问:

{
  "src": [
    "pages/Index",
    "pages/ColumnStartDemo"
  ]
}

2.2 SDK 版本配置

在根目录的 build-profile.json5 中指定了目标 SDK 版本:

{
  "app": {
    "products": [
      {
        "name": "default",
        "targetSdkVersion": "6.1.1(24)",
        "compatibleSdkVersion": "6.1.1(24)",
        "runtimeOS": "HarmonyOS"
      }
    ]
  }
}

这意味着我们的应用只能在 HarmonyOS NEXT 6.1.1 及以上版本运行,可以充分利用 API 24 提供的新特性。


3. Column 容器布局原理

3.1 Column 是什么

Column 是 ArkTS 中最基础的垂直布局容器。它的工作方式类似于 CSS Flexbox 中的 flex-direction: column——所有子组件沿着垂直方向依次排列。

┌─────────────────────┐
│  ┌─────────────────┐│
│  │  子组件 1       ││  ← 顶部
│  └─────────────────┘│
│  ┌─────────────────┐│
│  │  子组件 2       ││
│  └─────────────────┘│
│  ┌─────────────────┐│
│  │  子组件 3       ││  ← 底部
│  └─────────────────┘│
└─────────────────────┘

3.2 Column 的核心属性

Column 容器主要提供两个布局控制维度:

属性 作用维度 可选值 说明
alignItems 水平方向(X轴) ItemAlign.Start / Center / End / Stretch / Baseline 控制子组件在水平方向的对齐方式
justifyContent 垂直方向(Y轴) FlexAlign.Start / Center / End / SpaceBetween / SpaceAround / SpaceEvenly 控制子组件在垂直方向的排列方式

3.3 类比 CSS Flexbox

对于有 Web 开发经验的读者,理解 Column 的布局体系会更轻松:

ArkTS CSS Flexbox 等价
Column display: flex; flex-direction: column
Column { Row() } flex-direction: column 内嵌 flex-direction: row
alignItems(ItemAlign.Start) align-items: flex-start
alignItems(ItemAlign.Center) align-items: center
alignItems(ItemAlign.End) align-items: flex-end
justifyContent(FlexAlign.Start) justify-content: flex-start
justifyContent(FlexAlign.SpaceBetween) justify-content: space-between
.width('100%') width: 100%
.padding() padding
.margin() margin

4. ColumnStart 布局详解(核心)

4.1 定义

所谓 ColumnStart 布局,指的是:

Column() {
  // 子组件列表...
}
.width('100%')
.alignItems(ItemAlign.Start)       // ← 水平方向左对齐
.justifyContent(FlexAlign.Start)   // ← 垂直方向顶部对齐

4.2 布局效果

  • 水平方向:所有子组件的左侧边缘对齐到容器的左侧
  • 垂直方向:子组件从容器的顶部开始依次排列

视觉效果:

┌──────────────────────────────────┐
│子组件 1(宽度自适应)            │  ← 顶部开始
│子组件 2(宽度自适应)            │
│子组件 3(宽度自适应)            │
│                                  │  ← 底部留空
└──────────────────────────────────┘

4.3 完整的最小示例

最简单最纯粹的 ColumnStart 布局只需要几行代码:

@Entry
@Component
struct SimpleColumnStart {
  build() {
    Column() {
      Text('标题')
        .fontSize(20)
        .fontWeight(FontWeight.Bold)

      Text('这是一段描述文字,用于说明布局效果。')
        .fontSize(14)
        .lineHeight(22)

      Button('确认')
        .width(120)
        .height(40)
    }
    .width('100%')
    .height('100%')
    .alignItems(ItemAlign.Start)       // 水平左对齐
    .justifyContent(FlexAlign.Start)   // 垂直顶部对齐
    .padding(20)
  }
}

运行效果:三个组件(Text → Text → Button)全部靠左上角排列,从上往下依次显示。

4.4 为什么 ColumnStart 如此重要

在移动应用中,绝大多数页面布局都属于 「从上往下读」 的模式:

  • 列表页:标题在顶部,列表项依次往下排列
  • 详情页:标题、作者、正文、标签从上到下
  • 表单页:标签 + 输入框成对出现,从上到下排列
  • 个人中心:头像、昵称、统计数据、功能菜单依次排列

ColumnStart 正好满足这种自然的阅读顺序,因此它是 ArkTS 开发中最常用、最基础的布局模式。


5. alignItems 三种取值效果对比

理解 alignItems 的不同取值,是掌握 Column 布局的关键。我们在 ColumnStartDemo.ets 中专门构建了一个对比区块,使用三个等大的彩色方块(A、B、C)来直观展示差异。

5.1 ItemAlign.Start(左对齐)

Column() {
  this.buildDemoBlock('A', '#FF6B6B')  // 红色方块
  this.buildDemoBlock('B', '#4ECDC4')  // 青色方块
  this.buildDemoBlock('C', '#45B7D1')  // 蓝色方块
}
.width('100%')
.alignItems(ItemAlign.Start)           // ← 左对齐

效果:

┌──────────────────────────────────────┐
│[A████████]                           │  ← 全部靠左
│[B████████]                           │
│[C████████]                           │
└──────────────────────────────────────┘

所有子组件的左边缘对齐到容器左边缘。由于方块宽度固定(80vp),它们整齐地排列在左侧。

5.2 ItemAlign.Center(居中对齐)

Column() {
  this.buildDemoBlock('A', '#FF6B6B')
  this.buildDemoBlock('B', '#4ECDC4')
  this.buildDemoBlock('C', '#45B7D1')
}
.width('100%')
.alignItems(ItemAlign.Center)          // ← 居中对齐

效果:

┌──────────────────────────────────────┐
│              [A████████]             │  ← 全部居中
│              [B████████]             │
│              [C████████]             │
└──────────────────────────────────────┘

所有子组件的中心线对齐到容器中心。对于宽度不一致的子组件,它们会以中线为准对齐,左右边距不对称。

5.3 ItemAlign.End(右对齐)

Column() {
  this.buildDemoBlock('A', '#FF6B6B')
  this.buildDemoBlock('B', '#4ECDC4')
  this.buildDemoBlock('C', '#45B7D1')
}
.width('100%')
.alignItems(ItemAlign.End)             // ← 右对齐

效果:

┌──────────────────────────────────────┐
│                           [A████████]│  ← 全部靠右
│                           [B████████]│
│                           [C████████]│
└──────────────────────────────────────┘

所有子组件的右边缘对齐到容器右边缘。这种布局在常规页面中较少使用,但适用于特定的设计需求(如右上角的操作菜单)。

5.4 对比总结

取值 对齐基准 适用场景
ItemAlign.Start 左边缘 信息流列表、表单、文章详情、设置页
ItemAlign.Center 中心线 弹窗、提示卡片、加载动画
ItemAlign.End 右边缘 右上角操作菜单、特殊对齐需求

5.5 关于 ItemAlign.Stretch

还有一个常用值 ItemAlign.Stretch,它会让子组件在水平方向拉伸填满容器宽度,相当于子组件的 width 变为 100%(如果子组件未设置固定宽度)。这在表单布局中非常有用:

Column() {
  TextInput({ placeholder: '输入框1' })
  TextInput({ placeholder: '输入框2' })
  Button('提交')
}
.width('100%')
.alignItems(ItemAlign.Stretch)   // 所有子组件拉伸填满

6. justifyContent 垂直对齐控制

justifyContent 控制子组件在垂直方向(Column 的主轴方向)的排列方式。

6.1 FlexAlign.Start(顶部对齐)

Column() {
  Text('顶部对齐')
  Text('内容紧贴顶部')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Start)

效果:子组件从容器顶部开始排列,底部留空。

6.2 FlexAlign.Center(垂直居中)

Column() {
  Text('居中显示')
  Text('内容在正中间')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)

效果:子组件的整体在容器垂直中央。

6.3 FlexAlign.End(底部对齐)

Column() {
  Text('底部对齐')
  Text('内容紧贴底部')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.End)

效果:子组件排列在容器底部。

6.4 FlexAlign.SpaceBetween(两端对齐)

Column() {
  Text('顶部')
  Text('中间')
  Text('底部')
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.SpaceBetween)

效果:第一个子组件在顶部,最后一个在底部,剩余空间均匀分配给元素之间的间隙。

6.5 FlexAlign.SpaceEvenly(均匀分布)

Column()
.justifyContent(FlexAlign.SpaceEvenly)

效果:所有间隙(包括顶部和底部边缘到第一个/最后一个元素的距离)完全相等。

6.6 实际应用建议

在 ColumnStart 布局中,我们使用 FlexAlign.Start 作为默认值,因为大多数页面都是从顶部开始阅读的。但某些场景下可以灵活变化:

  • 个人中心页顶部FlexAlign.Start,信息从上往下排列
  • 对话弹窗FlexAlign.Center,内容居中显示
  • 底部操作栏FlexAlign.End,按钮固定在底部

7. 实战场景一:信息流列表

信息流列表是 ColumnStart 最经典的应用场景。在 ColumnStartDemo.ets 中,我们构建了 5 条模拟信息流数据,每条卡片内部也采用 ColumnStart 布局。

7.1 数据模型

首先定义数据结构:

interface FeedItem {
  id: number;
  title: string;
  summary: string;
  tag: string;
  time: string;
  icon: string;
}

7.2 状态数据

使用 @State 装饰器声明响应式数据:

@State feedList: FeedItem[] = [
  {
    id: 1,
    title: 'HarmonyOS NEXT 正式发布',
    summary: '全新鸿蒙内核,不再兼容 Android 应用,全栈自研操作系统面向万物互联。',
    tag: '资讯',
    time: '10 分钟前',
    icon: '🚀'
  },
  {
    id: 2,
    title: 'ArkTS 声明式 UI 最佳实践',
    summary: '学习如何使用 @State、@Prop、@Link 装饰器管理组件状态。',
    tag: '教程',
    time: '1 小时前',
    icon: '📘'
  },
  // ... 更多数据
];

7.3 卡片布局(双重复合 ColumnStart)

每张信息流卡片内部也采用 ColumnStart 布局,形成 「外层 Column 列表 → 内层 Column 卡片」 的双层结构:

@Builder
buildFeedCard(item: FeedItem): void {
  Column() {
    // ─ 第一行:图标 + 标题 ─
    Row() {
      Text(item.icon)
        .fontSize(24)
        .margin({ right: 10 })

      Text(item.title)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .fontColor('#2C3E50')
    }
    .margin({ bottom: 6 })

    // ─ 摘要(左对齐,自动换行) ─
    Text(item.summary)
      .fontSize(13)
      .fontColor('#636E72')
      .lineHeight(20)
      .margin({ bottom: 10 })

    // ─ 标签 + 时间 ─
    Row() {
      Text(item.tag)
        .fontSize(11)
        .fontColor('#FFFFFF')
        .backgroundColor('#4ECDC4')
        .padding({ left: 8, right: 8, top: 2, bottom: 2 })
        .borderRadius(10)

      Text('·')
        .fontSize(11)
        .fontColor('#BDC3C7')
        .margin({ left: 8, right: 8 })

      Text(item.time)
        .fontSize(11)
        .fontColor('#BDC3C7')
    }
  }
  .width('100%')
  .alignItems(ItemAlign.Start)          // ← 卡片内左对齐
  .padding(14)
  .backgroundColor('#FFFFFF')
  .borderRadius(12)
  .border({ width: 1, color: '#EEEEEE' })
  .margin({ bottom: 10 })
  .onClick(() => {
    this.onCardTap(item);
  })
}

7.4 列表渲染

使用 ForEach 循环渲染:

ForEach(this.feedList, (item: FeedItem) => {
  this.buildFeedCard(item)
}, (item: FeedItem) => item.id.toString())

7.5 布局分析

每张卡片内部的布局结构:

┌──────────────────────────────────────┐
│🚀 HarmonyOS NEXT 正式发布            │  ← Row (图标 + 标题)
│全新鸿蒙内核,不再兼容 Android 应用... │  ← Text (摘要,多行)
│[资讯] · 10 分钟前                    │  ← Row (标签 + 时间)
└──────────────────────────────────────┘

所有内容的左边缘都在一条垂直线上,形成了清晰的视觉引导。这就是 ColumnStart 的威力——不需要复杂的嵌套对齐设置,所有子组件自然左对齐。


8. 实战场景二:表单页面

表单是 ColumnStart 的另一大典型场景。标签和输入框的「上下结构」天生适合 Column 布局。

8.1 表单容器

Column() {
  // ── 标签 ──
  Text('姓名')
    .fontSize(14)
    .fontWeight(FontWeight.Medium)
    .fontColor('#2C3E50')
    .margin({ bottom: 6 })

  // ── 输入框 ──
  TextInput({ placeholder: '请输入您的姓名', text: this.userName })
    .width('100%')
    .height(44)
    .padding({ left: 12 })
    .backgroundColor('#F8F9FA')
    .borderRadius(8)
    .onChange((val: string) => { this.userName = val; })
    .margin({ bottom: 16 })

  // ── 邮箱 ──
  Text('电子邮箱')
    .fontSize(14)
    .fontWeight(FontWeight.Medium)
    .fontColor('#2C3E50')
    .margin({ bottom: 6 })

  TextInput({ placeholder: '请输入邮箱地址', text: this.userEmail })
    .width('100%')
    .height(44)
    .padding({ left: 12 })
    .backgroundColor('#F8F9FA')
    .borderRadius(8)
    .onChange((val: string) => { this.userEmail = val; })
    .margin({ bottom: 16 })

  // ── 提交按钮 ──
  Button('提交信息')
    .width('100%')
    .height(48)
    .backgroundColor('#4ECDC4')
    .fontColor('#FFFFFF')
    .fontSize(16)
    .borderRadius(24)
    .onClick(() => { this.onSubmitForm(); })
}
.width('100%')
.padding(16)
.backgroundColor('#FFFFFF')
.borderRadius(12)
.border({ width: 1, color: '#E8E8E8' })

8.2 布局分析

┌──────────────────────────────────────┐
│姓名                                  │  ← 标签顶部对齐
│[┌────────────────────────────────┐]  │  ↓
│[│  请输入您的姓名                │]  │  输入框
│[└────────────────────────────────┘]  │  ↓
│电子邮箱                              │
│[┌────────────────────────────────┐]  │
│[│  请输入邮箱地址                │]  │
│[└────────────────────────────────┘]  │
│手机号(选填)                        │
│[┌────────────────────────────────┐]  │
│[│  请输入手机号                  │]  │
│[└────────────────────────────────┘]  │
│                                      │
│[┌════════════════════════════════┐]  │
│[║          提交信息              ║]  │  ← 按钮宽度 100%
│[└════════════════════════════════┘]  │
└──────────────────────────────────────┘

8.3 关键设计要点

  1. 标签在上,输入框在下:每个表单项是一个「标签 + 输入框」的垂直组合
  2. 标签左对齐:Text 组件默认左对齐,与 ColumnStart 一致
  3. 输入框宽度 100%:通过 .width('100%') 撑满容器,确保表单整齐
  4. 输入框类型:手机号输入框使用 .type(InputType.PhoneNumber) 调用数字键盘
  5. 圆角设计:使用 .borderRadius() 营造现代 UI 风格

8.4 表单验证

private onSubmitForm(): void {
  if (!this.userName.trim() || !this.userEmail.trim()) {
    promptAction.showToast({
      message: '请填写姓名和邮箱',
      duration: 1500
    });
    return;
  }
  promptAction.showToast({
    message: `已提交 ${this.userName} 的表单`,
    duration: 2000
  });
}

9. 实战场景三:番茄钟应用

Index.ets 是一个完整的番茄钟(Pomodoro Timer)应用,虽然它的主布局使用的是 alignItems(HorizontalAlign.Center)(居中对齐),而不是 Start,但它的整体结构仍然是 Column 垂直布局。这个例子展示了 Column 布局在实际应用中的灵活运用。

9.1 布局结构

build() {
  Column() {
    // ① 顶部标题
    Text('🍅 番茄钟')

    // ② 阶段标签("专注工作" / "休息时间")
    Text(this.phaseText)

    // ③ 计时器圆环(Canvas 绘制)
    Stack() { Canvas(this.ctx) }

    // ④ 控制按钮(Row 水平布局)
    Row() { Button('▶ 开始'); Button('↻ 重置') }

    // ⑤ 已完成番茄数
    Row() { /* 番茄图标列表 */ }
    Text(`已完成 ${this.completedSessions} 个番茄`)

    // ⑥ 提示文字
    Text('专注 25 分钟,休息 5 分钟')
  }
  .width('100%')
  .height('100%')
  .alignItems(HorizontalAlign.Center)  // ← 这里用了 Center
}

9.2 为什么番茄钟使用 Center 而不是 Start

番茄钟是一个 「焦点型」 应用——用户盯着计时器看,所有 UI 元素围绕计时器在视觉中心布局。因此采用居中布局更合理:

         ┌──────────────────────┐
         │       🍅 番茄钟       │
         │      专注工作         │
         │                       │
         │    ┌───────────┐      │
         │    │  25:00    │      │  ← 计时器居中
         │    │  ● 进行中 │      │
         │    └───────────┘      │
         │                       │
         │  [▶ 开始]  [↻ 重置]  │
         │    🍅 ○ ○ ○           │
         └──────────────────────┘

这正好说明:没有绝对的"最佳布局",只有最适合场景的布局

9.3 Canvas 绘制计时圆环

番茄钟的核心是使用 Canvas 组件绘制的环形进度条:

private drawRing(): void {
  const ctx = this.ctx;
  const cx = this.CX;      // 圆心 X: 120
  const cy = this.CY;      // 圆心 Y: 120
  const r = this.RING_RADIUS;  // 半径: 100
  const lw = this.RING_WIDTH;  // 线宽: 10

  // ① 清空画布
  ctx.clearRect(0, 0, this.RING_SIZE, this.RING_SIZE);

  // ② 绘制背景圆环(浅色)
  ctx.beginPath();
  ctx.arc(cx, cy, r, 0, Math.PI * 2);
  ctx.strokeStyle = this.secondaryColor;  // #FFE0E0 或 #D4F5F2
  ctx.lineWidth = lw;
  ctx.stroke();

  // ③ 绘制进度弧(主色)
  if (this.progress > 0) {
    const startAngle = -Math.PI / 2;  // 从12点钟方向开始
    const sweepAngle = Math.PI * 2 * this.progress;
    const endAngle = startAngle + sweepAngle;

    ctx.beginPath();
    ctx.arc(cx, cy, r, startAngle, endAngle);
    ctx.strokeStyle = this.primaryColor;  // #FF6B6B 或 #4ECDC4
    ctx.lineWidth = lw;
    ctx.lineCap = 'round';  // 圆角端点
    ctx.stroke();
  }

  // ④ 绘制中央时间文字
  ctx.font = '48px sans-serif';
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.fillStyle = '#2D3436';
  ctx.fillText(this.timeDisplay, cx, cy - 8);

  // ⑤ 绘制状态文字
  ctx.font = '14px sans-serif';
  ctx.fillStyle = '#636E72';
  if (this.showFinishTip) {
    ctx.fillText('⏰ 时间到!', cx, cy + 32);
  } else if (this.isRunning) {
    ctx.fillText('● 进行中', cx, cy + 32);
  } else if (this.remainingTime < this.totalTime) {
    ctx.fillText('⏸ 已暂停', cx, cy + 32);
  }
}

9.4 状态管理

番茄钟使用多个 @State 装饰器管理的响应式状态变量:

@State remainingTime: number = 25 * 60;    // 剩余时间(秒)
@State isRunning: boolean = false;          // 是否运行中
@State isWorkPhase: boolean = true;         // 工作/休息阶段
@State completedSessions: number = 0;       // 已完成番茄数
@State phaseText: string = '专注工作';      // 阶段文字
@State showFinishTip: boolean = false;      // 是否显示完成提示

9.5 定时器逻辑

使用 setInterval 实现每秒倒计时:

private startTimer(): void {
  if (this.isRunning) return;
  this.isRunning = true;
  this.showFinishTip = false;

  this.timerId = setInterval(() => {
    let next = this.remainingTime - 1;
    if (next <= 0) {
      this.remainingTime = 0;
      this.isRunning = false;
      this.clearTimer();
      this.onPhaseComplete();
    } else {
      this.remainingTime = next;
    }
    this.drawRing();  // 每秒钟重绘一次圆环
  }, 1000);
}

9.6 阶段切换

private onPhaseComplete(): void {
  this.showFinishTip = true;

  if (this.isWorkPhase) {
    // 工作完成 → 进入休息
    this.completedSessions++;
    this.isWorkPhase = false;
    this.phaseText = '休息时间';
    this.remainingTime = this.BREAK_TIME;  // 5分钟
  } else {
    // 休息完成 → 进入工作
    this.isWorkPhase = true;
    this.phaseText = '专注工作';
    this.remainingTime = this.WORK_TIME;   // 25分钟
  }
  this.drawRing();
}

10. @Builder 装饰器:复用布局组件

ColumnStartDemo.ets 中,我们大量使用了 @Builder 装饰器来创建可复用的布局组件。这是 ArkTS 提升代码复用性的重要特性。

10.1 @Builder 基础用法

@Builder 用于定义非 build() 方法内的 UI 构建函数,可以在 build() 方法中多次调用:

@Builder
buildDemoBlock(label: string, color: string): void {
  Text(label)
    .fontSize(16)
    .fontWeight(FontWeight.Bold)
    .fontColor('#FFFFFF')
    .width(80)
    .height(44)
    .textAlign(TextAlign.Center)
    .backgroundColor(color)
    .borderRadius(6)
    .margin({ bottom: 6 })
}

然后在 build() 中调用:

Column() {
  this.buildDemoBlock('A', '#FF6B6B')
  this.buildDemoBlock('B', '#4ECDC4')
  this.buildDemoBlock('C', '#45B7D1')
}

10.2 @Builder 的三个优势

  1. 去重:同样的卡片或区块不需要重复写相同的代码
  2. 参数化:通过函数参数控制颜色、文字、数据等,灵活适应不同场景
  3. 作用域内:@Builder 方法可以访问 struct 的所有成员变量和 @State 状态

10.3 本项目中定义的 @Builder 方法

方法名 用途 参数
buildDemoBlock 构建对比演示方块 label: string, color: string
buildFeedCard 构建信息流卡片 item: FeedItem
buildTipRow 构建布局要点提示行 text: string

10.4 @Builder 与 @Component 的选择

特性 @Builder @Component
独立 struct 否,定义在父组件内 是,独立的 struct
访问父组件状态 可直接访问 需通过 @Prop/@Link 传入
复用范围 当前 struct 内 全局复用
性能 轻量,无创建开销 有组件实例化开销
适用场景 页面内的局部复用 跨页面复用的独立组件

11. 路由与页面导航

11.1 导入路由模块

import router from '@ohos.router';

11.2 跳转到新页面

Button('📐 查看 ColumnStart 布局演示')
  .onClick(() => {
    router.pushUrl({ url: 'pages/ColumnStartDemo' });
  })

11.3 返回上一页

private goBack(): void {
  router.back();
}

11.4 路由栈机制

router.pushUrl() 将新页面压入路由栈,保留当前页面。用户可以通过 router.back() 逐级返回。这种机制非常适合:

  • 主页面 → 详情页:主页面保留在栈中,返回时不丢失状态
  • 列表页 → 详情页:从详情页返回列表页时,列表可以保持滚动位置

11.5 页面生命周期

当使用路由导航时,页面的生命周期回调会被触发:

Index(首页)
  │
  ├── aboutToAppear()    ← 首页创建
  │
  ├── router.pushUrl()   ← 跳转到 ColumnStartDemo
  │
  │   ColumnStartDemo
  │   ├── aboutToAppear()
  │   └── (用户操作)
  │
  ├── router.back()      ← 返回首页
  │
  └── ColumnStartDemo.aboutToDisappear()

12. 布局要点总结

12.1 ColumnStart 核心配置

Column() {
  // 子组件列表...
}
.width('100%')
.alignItems(ItemAlign.Start)       // 水平左对齐
.justifyContent(FlexAlign.Start)   // 垂直顶部对齐

12.2 代码对比速查

布局名称 alignItems justifyContent 效果
ColumnStart ItemAlign.Start FlexAlign.Start 左上对齐
ColumnCenter ItemAlign.Center FlexAlign.Start 居中对齐
ColumnEnd ItemAlign.End FlexAlign.Start 右上对齐
居中布局 ItemAlign.Center FlexAlign.Center 正中央
底部居左 ItemAlign.Start FlexAlign.End 左下角

12.3 常用属性速查

Column()
  .width('100%')              // 宽度填满父容器
  .height('100%')             // 高度填满父容器
  .alignItems(ItemAlign.Start)  // 水平对齐方式
  .justifyContent(FlexAlign.Start) // 垂直排列方式
  .padding(16)                // 内边距
  .backgroundColor('#F5F6FA')  // 背景色
  .borderRadius(12)           // 圆角
  .border({ width: 1, color: '#EEEEEE' }) // 边框

12.4 嵌套 Column 的最佳实践

当需要多层嵌套时,每层 Column 可以独立设置对齐方式:

Column() {                         // 外层:Start
  Column() {                       // 内层:Center(覆盖外层)
    Text('居中的内容')
  }
  .width('100%')
  .alignItems(ItemAlign.Center)

  Column() {                       // 内层:继承外层的 Start
    Text('左对齐的内容')
  }
  .width('100%')
  // 未设置 alignItems,继承外层 Column 的 ItemAlign.Start
}
.width('100%')
.alignItems(ItemAlign.Start)

13. 常见问题与调试技巧

13.1 子组件没有按预期对齐

症状:设置了 alignItems(ItemAlign.Start),但子组件没有左对齐。

原因排查

  1. 子组件设置了固定宽度:如果子组件宽度 = 父容器宽度,那么 Start/Center/End 的效果看起来完全一样

    • 解决:给子组件设置一个小于父容器的宽度,或使用 width('auto')
  2. 子组件使用了 .width('100%'):这和原因 1 一样,填满了就没有对齐空间了

    • 解决:对于需要左对齐的组件,去掉 .width('100%'),或者只给内容所需宽度
  3. 在 Row 容器中设置了 alignItemsRowalignItems 控制垂直方向(Cross Axis),不是水平方向

    • 解决:Row 中使用 alignItems(VerticalAlign.Center) 控制垂直居中

13.2 高度为 0 的问题

症状:Column 容器内没有组件时高度为 0,背景颜色不显示。

原因:Column 的高度默认由子组件撑起。没有子组件或子组件高度为 0 时,Column 高度为 0。

解决

.height('100%')       // 填满父容器
// 或
.constraintSize({ minHeight: 200 })  // 设置最小高度

13.3 超出屏幕无法滚动

症状:Column 内子组件过多,超出屏幕高度无法显示。

解决:使用 Scroll 包裹 Column,使页面可滚动:

Scroll() {
  Column() {
    // 很多子组件...
  }
  .width('100%')
}
.width('100%')
.height('100%')
.scrollBar(BarState.Off)  // 可选:隐藏滚动条

13.4 Column 嵌套卡顿

症状:多层 Column 嵌套导致 UI 卡顿。

原因:过多的布局嵌套增加了布局计算的开销。

建议

  • 嵌套不超过 3-4 层
  • 尽量使用 @Builder 拆分子布局
  • 列表使用 ForEach + 卡片组件,避免单个 Column 包含上百个节点
  • 考虑使用 ListItem + List 组件优化长列表性能

13.5 调试工具使用

HarmonyOS NEXT 的 DevEco Studio 提供了强大的布局调试工具:

  1. Inspector(预览检查器):在 Previewer 中点击组件,查看其布局属性
  2. 布局边界绘制:在开发者选项中开启「显示布局边界」,直观看到每个组件的 padding、margin
  3. console.info 日志:在生命周期回调中打印日志,确认组件加载状态
aboutToAppear(): void {
  console.info('[ColumnStartDemo] 页面初始化完成');
}

14. 结语

14.1 全文回顾

本文通过一个完整可运行的 HarmonyOS NEXT 应用,深入讲解了 ColumnStart 垂直排列布局 的核心概念、配置方法和实际应用。

三个关键知识点:

  1. Column 容器:ArkTS 中最基础的垂直布局容器,子组件沿垂直方向排列
  2. alignItems(ItemAlign.Start):控制子组件在水平方向左对齐
  3. justifyContent(FlexAlign.Start):控制子组件在垂直方向顶部对齐

三个实战场景:

场景 布局特点 关键组件
信息流列表 双层 ColumnStart,每张卡片内部左对齐 Column + ForEach + @Builder
表单页面 标签在上、输入框在下,左对齐 Column + TextInput + Button
番茄钟 Column 居中布局,Canvas 绘制 Column + Canvas + setInterval

14.2 布局选择指南

在实际项目中,应该如何选择布局方式?

需要什么布局?           → 使用什么容器?
─────────────────────────────────────────
垂直排列,顶部对齐       → Column + alignItems(Start)
垂直排列,居中           → Column + alignItems(Center)
垂直排列,左对齐+拉伸    → Column + alignItems(Stretch)
水平排列,左对齐         → Row + alignItems(Center)
水平排列,两端对齐       → Row + justifyContent(SpaceBetween)
网格布局                 → Grid / GridRow
层叠布局                 → Stack
灵活弹性布局             → Flex

14.3 从本示例延伸

掌握了 ColumnStart 布局之后,你可以:

  • 修改 alignItemsjustifyContent 的值,观察布局变化
  • 在内层卡片中尝试不同的对齐方式组合
  • 将信息流卡片替换为实际业务数据
  • 添加下拉刷新、上拉加载等功能
  • 使用 @Component 将卡片抽取为独立的组件文件

14.4 完整代码获取

本文所有源代码均位于项目的以下文件中:

  • 首页(番茄钟)entry/src/main/ets/pages/Index.ets
  • 布局演示页entry/src/main/ets/pages/ColumnStartDemo.ets
  • 页面路由entry/src/main/resources/base/profile/main_pages.json
  • 应用配置AppScope/app.json5
  • SDK 配置build-profile.json5

将项目导入 DevEco Studio 后,即可编译运行到 HarmonyOS NEXT 真机或模拟器上查看效果。


本文基于 HarmonyOS NEXT 6.1.1(API 24)Stage 模型编写
所有代码均通过 DevEco Studio 编译验证

欢迎使用Markdown编辑器

你好! 这是你第一次使用 Markdown编辑器 所展示的欢迎页。如果你想学习如何使用Markdown编辑器, 可以仔细阅读这篇文章,了解一下Markdown的基本语法知识。

新的改变

我们对Markdown编辑器进行了一些功能拓展与语法支持,除了标准的Markdown编辑器功能,我们增加了如下几点新功能,帮助你用它写博客:

  1. 全新的界面设计 ,将会带来全新的写作体验;
  2. 在创作中心设置你喜爱的代码高亮样式,Markdown 将代码片显示选择的高亮样式 进行展示;
  3. 增加了 图片拖拽 功能,你可以将本地的图片直接拖拽到编辑区域直接展示;
  4. 全新的 KaTeX数学公式 语法;
  5. 增加了支持甘特图的mermaid语法1 功能;
  6. 增加了 多屏幕编辑 Markdown文章功能;
  7. 增加了 焦点写作模式、预览模式、简洁写作模式、左右区域同步滚轮设置 等功能,功能按钮位于编辑区域与预览区域中间;
  8. 增加了 检查列表 功能。

功能快捷键

撤销:Ctrl/Command + Z
重做:Ctrl/Command + Y
加粗:Ctrl/Command + B
斜体:Ctrl/Command + I
标题:Ctrl/Command + Shift + H
无序列表:Ctrl/Command + Shift + U
有序列表:Ctrl/Command + Shift + O
检查列表:Ctrl/Command + Shift + C
插入代码:Ctrl/Command + Shift + K
插入链接:Ctrl/Command + Shift + L
插入图片:Ctrl/Command + Shift + G
查找:Ctrl/Command + F
替换:Ctrl/Command + G

合理的创建标题,有助于目录的生成

直接输入1次#,并按下space后,将生成1级标题。
输入2次#,并按下space后,将生成2级标题。
以此类推,我们支持6级标题。有助于使用TOC语法后生成一个完美的目录。

如何改变文本的样式

强调文本 强调文本

加粗文本 加粗文本

标记文本

删除文本

引用文本

H2O is是液体。

210 运算结果是 1024.

插入链接与图片

链接: link.

图片: Alt

带尺寸的图片: Alt

居中的图片: Alt

居中并且带尺寸的图片: Alt

当然,我们为了让用户更加便捷,我们增加了图片拖拽功能。

如何插入一段漂亮的代码片

博客设置页面,选择一款你喜欢的代码片高亮样式,下面展示同样高亮的 代码片.

// An highlighted block
var foo = 'bar';

生成一个适合你的列表

  • 项目
    • 项目
      • 项目
  1. 项目1
  2. 项目2
  3. 项目3
  • 计划任务
  • 完成任务

创建一个表格

一个简单的表格是这么创建的:

项目 Value
电脑 $1600
手机 $12
导管 $1

设定内容居中、居左、居右

使用:---------:居中
使用:----------居左
使用----------:居右

第一列 第二列 第三列
第一列文本居中 第二列文本居右 第三列文本居左

SmartyPants

SmartyPants 是一个文本转换工具,主要功能是将普通的 ASCII 标点符号自动转换为更美观的印刷体标点符号。例如:

原始符号 转换后 说明
"引号" “引号” 直引号变弯引号
'单引号' ‘单引号’ 直单引号变弯单引号
-- 两个连字符变短破折号
--- 三个连字符变长破折号
... 三个点变省略号

创建一个自定义列表

Markdown
Text-to- HTML conversion tool
Authors
John
Luke

如何创建一个注脚

一个具有注脚的文本。2

注释也是必不可少的

Markdown将文本转换为 HTML

KaTeX数学公式

您可以使用渲染LaTeX数学表达式 KaTeX:

Gamma公式展示 Γ ( n ) = ( n − 1 ) ! ∀ n ∈ N \Gamma(n) = (n-1)!\quad\forall n\in\mathbb N Γ(n)=(n1)!nN 是通过欧拉积分

Γ ( z ) = ∫ 0 ∞ t z − 1 e − t d t   . \Gamma(z) = \int_0^\infty t^{z-1}e^{-t}dt\,. Γ(z)=0tz1etdt.

你可以找到更多关于的信息 LaTeX 数学表达式here.

新的甘特图功能,丰富你的文章

2014-01-07 2014-01-09 2014-01-11 2014-01-13 2014-01-15 2014-01-17 2014-01-19 2014-01-21 已完成 进行中 计划一 计划二 现有任务 Adding GANTT diagram functionality to mermaid
  • 关于 甘特图 语法,参考 这儿,

UML图表

可以使用UML图表进行渲染,例如下面产生的一个序列图:

王五 李四 张三 王五 李四 张三 李四想了很长时间, 文字太长了 不适合放在一行. 你好!李四, 最近怎么样? 你最近怎么样,王五? 我很好,谢谢! 我很好,谢谢! 打量着王五... 很好... 王五, 你怎么样?
  • 关于 UML图表 语法,参考 这儿,

流程图

链接

长方形

圆角长方形

菱形

  • 关于 Mermaid 语法,参考 这儿,

FLowchart流程图

我们依旧会支持flowchart.js的流程图语法:

Created with Raphaël 2.3.0 开始 我的操作 确认? 结束 yes no
  • 关于 Flowchart流程图 语法,参考 这儿.

导出与导入

导出

如果你想尝试使用此编辑器, 你可以在此篇文章任意编辑。当你完成了一篇文章的写作, 在上方工具栏找到 文章导出 ,生成一个.md文件或者.html文件进行本地保存。

导入

如果你想加载一篇你写过的.md文件,在上方工具栏可以选择导入功能进行对应扩展名的文件导入,
继续你的创作。


  1. mermaid语法说明 ↩︎

  2. 注脚的解释 ↩︎

Logo

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

更多推荐