时间是移动端最基础也最频繁展示的信息之一。从锁屏界面的时间显示到状态栏的时钟、从世界时钟应用到会议排期工具,时间展示无处不在。HarmonyOS NEXT ArkUI 提供了 TextClock 组件,它自动获取系统时间并以文本形式渲染,开发者只需一行代码即可实现"活的时间"。

本文将深入讲解 TextClock 组件的核心 API,并通过一个"世界时钟"实战案例,展示时区切换、城市筛选、排序切换和时差对比等功能。

关键词:HarmonyOS、ArkUI、TextClock、文本时钟、世界时钟、时区、timeZoneOffset

一、TextClock 组件概览

TextClock 是 ArkUI 组件家族中专门用于显示系统时间的成员。与传统的 Text + 定时器组合不同,TextClock 内建了时间获取和刷新机制——组件自动订阅系统时间,每秒刷新一次,无需手动管理定时器。

1.1 TextClock vs Text + setInterval

很多开发者习惯用 Text 配合 setInterval 来显示时间:

// 传统方案:Text + setInterval
@State timeStr: string = '';
private timer: number = -1;

aboutToAppear(): void {
  this.timer = setInterval(() => {
    let now = new Date();
    this.timeStr = now.getHours() + ':' + now.getMinutes() + ':' + now.getSeconds();
  }, 1000);
}

aboutToDisappear(): void {
  clearInterval(this.timer);
}

这种方案有三个问题:

  1. 代码冗长:需要手动创建/销毁定时器,每次更新都要格式化时间字符串
  2. 不精确setInterval 不保证精确的 1000ms 间隔,长时间运行可能漂移
  3. 资源浪费:即使页面不可见(如切入后台),定时器仍在运行

TextClock 解决了这三个问题:

// TextClock 方案:一行搞定
TextClock()
  .fontSize(48)
  .fontColor('#1a1a2e')

TextClock 内部使用系统时间回调,精确且高效。页面不可见时自动暂停刷新,切回前台后立即更新到最新时间。

1.2 TextClock 与其他时间相关组件

组件 用途 时间来源 典型场景
TextClock 显示当前系统时间 系统时钟 锁屏、状态栏、世界时钟
TextTimer 显示倒计时/正计时 手动设置的起始值 秒杀倒计时、运动计时
TimePicker 用户选择时间 用户交互 闹钟设置、预约时间
DatePicker 用户选择日期 用户交互 生日输入、日期预约

TextClock 的角色是"只读时间展示"——它不需要用户输入,不需要手动设置,纯粹反映系统当前时间。

二、TextClock 核心 API

2.1 构造函数:timeZoneOffset

TextClock 的构造函数接受一个可选参数 timeZoneOffset,以毫秒为单位指定时区偏移:

// 默认:使用系统时区
TextClock()

// UTC+8(北京时间):8 小时 × 3600 秒 × 1000 毫秒
TextClock({ timeZoneOffset: 8 * 3600000 })

// UTC+0(伦敦时间)
TextClock({ timeZoneOffset: 0 })

// UTC-5(纽约时间)
TextClock({ timeZoneOffset: -5 * 3600000 })

timeZoneOffset 的正值表示东时区(比 UTC 早),负值表示西时区(比 UTC 晚)。北京在东八区,偏移为 +28800000 毫秒。

这个参数的强大之处在于:多个 TextClock 实例可以分别指定不同的时区偏移,在同一个页面中同时展示不同城市的时间——世界时钟应用的基础能力。

2.2 文本样式属性

TextClock 继承自 Text 组件的样式属性,支持:

TextClock({ timeZoneOffset: 8 * 3600000 })
  .fontSize(48)                        // 字体大小
  .fontColor('#1a1a2e')               // 字体颜色
  .fontWeight(FontWeight.Bold)         // 字体粗细
  .fontFamily('monospace')            // 字体族(等宽字体适合数字)

使用 monospace 等宽字体是一个常见的时钟设计技巧——避免数字宽度变化导致的"抖动"。在等宽字体中,"1"和"8"占用相同宽度,时间的每个字符位置固定,视觉更稳定。

2.3 刷新机制

TextClock 的刷新时机由系统决定,开发者不需要干预。组件在以下情况会更新显示:

  1. 秒级刷新:时间变化时(通常每秒一次)
  2. 时区变化:系统检测到时区设置变更
  3. 前台恢复:从后台切回前台时立即更新

在页面不可见期间(如导航到其他页面),TextClock 会暂停刷新以节省资源。这是与手动 setInterval 方案的关键区别——setInterval 在后台仍会执行,浪费 CPU 和电量。

2.4 API 24 版本的特性与局限

在 API 24(HarmonyOS NEXT)版本中,TextClock 提供以下能力:

  • timeZoneOffset:通过构造函数参数指定时区偏移
  • 文本样式:继承 Text 的所有样式属性(fontSize、fontColor 等)
  • 自动刷新:系统级时间同步,前台精确刷新

当前版本需要注意的点:

  1. 不支持自定义格式:TextClock 使用系统默认的时间格式,开发者无法通过 format 属性指定自定义格式——格式跟随系统区域设置。中文系统下通常显示为"14:30:45"或"14:30"格式
  2. 不支持日期显示:TextClock 仅显示时间部分,不包含日期
  3. 只读组件:TextClock 没有交互能力(如点击选择时间),需要时间选择的场景应使用 TimePicker

这些限制在后续 API 版本中可能会放宽,但在当前版本(API 24)中需要注意选型。
在这里插入图片描述

三、实战:世界时钟应用

我们构建一个"世界时钟"页面,覆盖全球 8 个主要城市(北京、东京、悉尼、迪拜、伦敦、巴黎、纽约、洛杉矶),支持点击切换主时钟、添加/移除城市、按时区排序和时差对比。

3.1 数据模型

每个城市包含名称、时区标识、偏移量(毫秒)和配色:

interface CityData {
  name: string;      // 城市名称
  region: string;    // 时区标识(如 UTC+8)
  offsetMs: number;  // 时区偏移(毫秒)
  color: string;     // 卡片配色
}

private allCities: CityData[] = [
  { name: '北京', region: 'UTC+8', offsetMs: 8 * 3600000, color: '#FF6B6B' },
  { name: '东京', region: 'UTC+9', offsetMs: 9 * 3600000, color: '#E17055' },
  { name: '悉尼', region: 'UTC+10', offsetMs: 10 * 3600000, color: '#0984E3' },
  { name: '迪拜', region: 'UTC+4', offsetMs: 4 * 3600000, color: '#E09F3E' },
  { name: '伦敦', region: 'UTC+0', offsetMs: 0, color: '#6C5CE7' },
  { name: '巴黎', region: 'UTC+1', offsetMs: 1 * 3600000, color: '#00B894' },
  { name: '纽约', region: 'UTC-5', offsetMs: -5 * 3600000, color: '#2D3436' },
  { name: '洛杉矶', region: 'UTC-8', offsetMs: -8 * 3600000, color: '#636E72' },
];

每个城市使用独立的 color 值,在时钟卡片中起到视觉区分的作用——用户扫一眼颜色就能辨别城市,无需阅读文字。

时区偏移的范围从 UTC-8(洛杉矶)到 UTC+10(悉尼),覆盖了全球主要的时区跨度。8 个城市分布在 4 个大洲(亚洲、欧洲、北美洲、大洋洲),足以展示 TextClock 的时区能力。

3.2 主时钟显示

页面顶部有一个大型主时钟,显示当前选中城市的时间。点击下方任意城市卡片可切换主时钟的时区:

getSelectedCity(): CityData {
  if (this.visibleIds.indexOf(this.selectedCity) !== -1) {
    return this.allCities[this.selectedCity];
  }
  if (this.visibleIds.length > 0) {
    this.selectedCity = this.visibleIds[0];
    return this.allCities[this.selectedCity];
  }
  return this.allCities[0];
}

// 模板中使用:
TextClock({ timeZoneOffset: this.getSelectedCity().offsetMs })
  .fontSize(48)
  .fontColor('#1a1a2e')
  .fontWeight(FontWeight.Bold)
  .fontFamily('monospace')

主时钟的 timeZoneOffset 来自 getSelectedCity()——当用户点击不同城市卡片时,selectedCity 状态更新,主时钟自动切换为对应时区的时间。这是一个状态驱动的 UI 更新模式:状态(selectedCity)是单一数据源,UI(TextClock)无条件跟随。

主时钟下方显示时差标签,例如"比北京快 1 小时"(东京)或"比北京慢 13 小时"(纽约):

getOffsetLabel(ms: number): string {
  if (ms === this.localOffset) return '本地时间';
  let diff = ms - this.localOffset;
  let hours = diff / 3600000;
  if (hours > 0) {
    return '比北京快 '.concat(hours.toString()).concat(' 小时');
  }
  return '比北京慢 '.concat(Math.abs(hours).toString()).concat(' 小时');
}

这里以北京时间(UTC+8)为参照系,因为我们的目标用户是国内开发者,北京时间为"本地时间"是自然的基准。

3.3 城市筛选

8 个城市水平排列为可滚动的标签栏(Chip Row)。彩色标签表示已选中并展示的城市,灰色标签表示已移除的城市。点击标签切换显示/隐藏:

toggleCity(id: number): void {
  let idx = this.visibleIds.indexOf(id);
  if (idx !== -1) {
    this.visibleIds.splice(idx, 1);
    // 如果移除的是当前主时钟城市,自动切换到第一个可见城市
    if (this.selectedCity === id && this.visibleIds.length > 0) {
      this.selectedCity = this.visibleIds[0];
    }
  } else {
    this.visibleIds.push(id);
    // 保持排序规则
    if (this.sortByOffset) {
      this.visibleIds.sort((a, b) => this.allCities[a].offsetMs - this.allCities[b].offsetMs);
    } else {
      this.visibleIds.sort((a, b) => a - b);
    }
  }
}

选中城市标签使用该城市的主题色作为背景(如北京用 #FF6B6B 暖红),未选中标签使用灰色(#F2F3F5)。这种"彩色=活跃"的视觉语言让用户一眼就能辨别哪些城市正在展示。

关键边界处理:如果用户移除了当前主时钟所在的城市(如主时钟显示迪拜时间,但用户点击迪拜标签将其移除),代码自动将主时钟切换到第一个可见城市。这避免了主时钟指向一个不在列表中的城市的"空状态"。

3.4 排序切换

排序开关(Toggle)控制城市卡片的排列顺序。默认按原始顺序(大致按地理位置),开启后按时区偏移从小到大排列(从 UTC-8 洛杉矶到 UTC+10 悉尼):

getVisibleCities(): CityData[] {
  let result: CityData[] = [];
  for (let i = 0; i < this.allCities.length; i++) {
    if (this.visibleIds.indexOf(i) !== -1) {
      result.push(this.allCities[i]);
    }
  }
  if (this.sortByOffset) {
    result.sort((a, b) => a.offsetMs - b.offsetMs);
  }
  return result;
}

按时差排序后,卡片从左到右、从上到下呈现"从西到东"的地理顺序——洛杉矶(UTC-8)在左上角,悉尼(UTC+10)在右下角。这种排序方式帮助用户理解全球时区的渐变关系。

3.5 城市时钟卡片网格

使用 Grid 组件(2 列等分)展示每个可见城市的时钟卡片。每张卡片包含:城市名称 + 时区标识、TextClock 显示当地当前时间、时差标签:

Grid() {
  ForEach(this.getVisibleCities(), (city: CityData) => {
    GridItem() {
      Column() {
        Row() {
          Text(city.name).fontSize(14).fontColor('#1a1a2e').fontWeight(FontWeight.Medium)
          Blank()
          Text(city.region).fontSize(11).fontColor('#9999AA')
        }.width('100%').margin({ bottom: 6 })

        TextClock({ timeZoneOffset: city.offsetMs })
          .fontSize(20)
          .fontColor(city.color)
          .fontWeight(FontWeight.Bold)

        Text(this.getOffsetLabel(city.offsetMs))
          .fontSize(10).fontColor('#BBBBCC').margin({ top: 4 })
      }
      .width('100%').padding(12)
      .backgroundColor('#F2F3F5').borderRadius(10)
      .border({
        width: this.selectedCity === this.allCities.indexOf(city) ? 2 : 0,
        color: city.color,
        style: BorderStyle.Solid
      })
      .onClick(() => { this.selectedCity = this.allCities.indexOf(city); })
    }
  }, (city: CityData) => city.name)
}
.columnsTemplate('1fr 1fr').columnsGap(8).rowsGap(8)

当前选中的城市卡片用彩色边框(2vp 宽,城市主题色)高亮标识。边框颜色与城市主题色一致——例如选中北京时边框为暖红(#FF6B6B),选中纽约时边框为深灰(#2D3436)——保持视觉一致性。

3.6 页面完整结构

整个页面由 5 层组成:

build() {
  Column() {
    // 1. 标题栏 — 深蓝背景,白色"世界时钟"标题
    Row() { Text('世界时钟')... }.height(52).backgroundColor('#1a1a2e')

    Scroll() {
      Column() {
        // 2. 说明卡片 — TextClock 组件介绍
        Column() { ... }.backgroundColor('#FFFFFF')

        // 3. 主时钟区域 — 大号 TextClock + 时差标签
        Column() { TextClock({ timeZoneOffset: ... })... }.backgroundColor('#FFFFFF')

        // 4. 排序控件 + 城市标签栏
        Row() { Toggle + 已选计数 }.margin(...)
        Scroll() { Row() { 城市标签... } }

        // 5. 城市时钟卡片网格
        Column() {
          Grid() { ForEach...GridItem... }
        }
      }
    }
    .layoutWeight(1)
  }
}

在这里插入图片描述

四、世界时钟的交互设计

4.1 主时钟与卡片网格的关系

主时钟和卡片网格形成了"详情-列表"的经典交互模式:

  • 主时钟:聚焦展示一个城市的时间。大字号(48sp)、居中显示,适合快速瞥一眼
  • 卡片网格:同时展示多个城市的时间(20sp 字号),用于对比浏览

两者通过"点击卡片切换主时钟"关联——用户在网格中发现感兴趣的城市,点击后该城市"放大"到主时钟区域。这种模式类似于音乐播放器的"迷你播放器 + 全屏播放"布局,或者邮件应用的"邮件列表 + 详情面板"。

4.2 城市筛选的交互逻辑

默认展示全部 8 个城市。但在实际使用中,用户通常只关心 2-3 个城市(如"北京 + 纽约 + 伦敦"用于跨国会议排期)。通过筛选功能,用户可以:

  1. 移除不关心的城市,让网格更清爽
  2. 随时重新添加已移除的城市(标签栏保留所有 8 个城市的入口)
  3. 被移除的城市标签变灰,但名称和时区信息仍然可见

这种设计遵循了"渐进式展示"原则——默认显示全部信息,用户逐步筛选到自己的需求。标签栏始终提供全量城市的入口,避免用户"移除后找不到"的困惑。

4.3 时差标签的信息价值

每张卡片底部的时差标签(如"比北京慢 13 小时")是一个关键的 UX 细节。光看时间数字(如纽约显示"02:30")无法直观判断与本地时间的差距——需要心算"UTC-5 到 UTC+8 差几个时区"。时差标签直接给出换算结果,消除认知负担。

这就是"提供上下文信息"的交互设计原则:不要让用户在脑中进行数据换算,系统应该直接呈现换算结果。同样的道理适用于金额换算(“¥699 / 约 $96”)、距离换算(“距离 3.2km / 步行约 40 分钟”)等场景。

4.4 颜色编码策略

8 个城市使用 8 种不同的颜色编码,形成了一个"颜色-城市"的关联记忆:

  • 北京 → 暖红 (#FF6B6B)
  • 伦敦 → 紫色 (#6C5CE7)
  • 纽约 → 深灰 (#2D3436)
  • 悉尼 → 蓝色 (#0984E3)

用户使用几次后,会形成"红色=北京"的视觉惯性,扫一眼网格中红色的时钟就能快速定位北京时间。这种颜色编码比纯文字标签的识别速度快约 0.3-0.5 秒(依据 Healey 等人 1996 年的前注意视觉处理研究)——虽然差别微小,但在频繁浏览的场景中累积的体验提升是可感知的。

五、完整交互流程

  1. 初始状态:进入页面,主时钟显示北京时间,下方 8 张城市卡片全部可见,顶部标签栏全部彩色
  2. 切换主时钟:点击"纽约"卡片,主时钟更新为纽约当前时间,标签显示"比北京慢 13 小时",纽约卡片出现深灰边框
  3. 移除城市:点击顶部标签栏的"迪拜"标签,迪拜从卡片网格中消失,标签变为灰色
  4. 添加城市:再次点击"迪拜"标签,迪拜重新出现在网格中(按排序规则插入恰当位置)
  5. 按时差排序:打开"按时差排序"开关,卡片重新排列——洛杉矶(UTC-8)排第一,悉尼(UTC+10)排最后
  6. 对比浏览:扫视网格,每个城市的时间清晰可读,时差标签提供上下文对照

六、进阶扩展方向

6.1 自定义格式(后续 API 版本)

虽然 API 24 的 TextClock 不支持自定义格式,但后续版本可能会加入 format 属性。届时可以实现 12/24 小时制切换、显示/隐藏秒等功能:

// 未来可能的 API
TextClock({ format: 'HH:mm:ss' })  // 24 小时制带秒
TextClock({ format: 'hh:mm a' })   // 12 小时制带 AM/PM

6.2 时间差计算工具

基于当前的世界时钟 Demo,可以扩展一个"时差计算"功能:选择两个城市,自动计算两个时区之间的时间差,帮助用户确定合适的通话时间。例如:“北京时间 9:00 → 纽约时间 20:00(前一天)”。

6.3 自定义城市列表

将城市列表从硬编码改为用户可编辑的——添加任意城市、指定 UTC 偏移量、设置自定义名称和颜色。配合 PersistentStorage 持久化存储,实现真正的"我的世界时钟"。这是从 Demo 到产品的核心跨越。

6.4 日界线与工作日提示

在时差标签中加入日界线信息——例如"比北京慢 13 小时(前一天)“,帮助用户理解跨日界线的时间关系。进一步地,可以加入工作日/休息日提示(如"纽约:周一 20:30 / 工作日”),这对跨国商务沟通非常实用。

七、总结

TextClock 是 ArkUI 中最"省心"的组件之一。它不需要定时器、不需要格式化、不需要生命周期管理——一个构造函数参数(timeZoneOffset)就能覆盖全球 24 个时区的显示需求。

本文通过"世界时钟"这个实战案例,覆盖了:

  1. TextClock 与 Text + setInterval 方案的优劣对比——代码量、精度、资源管理
  2. timeZoneOffset 参数的使用和时区偏移计算——正值东时区、负值西时区
  3. 多个 TextClock 实例的并行使用——每个城市独立显示当地时间,互不干扰
  4. 城市筛选、排序切换和主时钟切换的交互实现
  5. 时差标签的 UX 价值——消除心算负担,提供上下文信息
  6. Grid 组件在时钟卡片布局中的二次应用

TextClock 的简洁性来自一个设计哲学:让系统做它擅长的事。系统最清楚当前时间是多少——让系统提供时间,开发者只需决定"在哪里展示、用什么样式展示、以哪个时区展示"。


Logo

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

更多推荐