一、引言时间是最基础的数据类型之一。在移动应用中无论是设置闹钟、选择提醒时间、记录事件时刻还是展示当前时间都离不开时间相关的交互与展示。HarmonyOS NEXT 的 ArkUI 框架为此提供了两个专门的组件TimePicker时间选择器和TextClock文本时钟。TimePicker 负责时间的选择——用户通过滚轮式的交互界面选择小时和分钟是一套完整的拾取器组件。TextClock 则负责时间的展示——它自动同步系统时钟以指定的格式实时显示当前时间无需开发者手动编写定时器或更新逻辑。两者虽然都围绕时间这一主题但在设计哲学上截然不同。TimePicker 是输入型组件关注用户交互和数据采集TextClock 是展示型组件关注数据呈现和自动更新。在实际开发中它们常常协同工作——比如闹钟应用顶部用 TextClock 展示当前时间下方用 TimePicker 让用户设定提醒时刻。本文将通过一个完整的**“闹钟提醒”**实战案例深入解析 TimePicker 和 TextClock 的核心 API、格式系统、交互模式和组合使用技巧。阅读完本文你将能够熟练掌握 TimePicker 的时间选择与数据获取理解 TextClock 的格式字符串体系与自动更新机制学会在同一个页面中组合使用 TimePicker 和 TextClock掌握提醒列表的数据管理和状态切换运用快捷预设提升用户操作效率二、TimePicker 时间选择器深入解析2.1 组件定位与基本概念TimePicker 是 ArkUI 中专门用于选择时间的拾取器组件。与 DatePicker日期选择器不同TimePicker 仅处理小时和分钟不涉及年月日。它的视觉呈现是一个垂直滚轮式界面用户可以上下滑动来切换小时和分钟的值。TimePicker 的设计源于一个简单但深刻的 UX 洞察让用户输入时间的最自然方式是选择而非键入。在传统表单中时间输入往往依赖两个 TextInput 框分别输入时和分用户需要切换焦点、处理格式校验体验非常碎片化。TimePicker 将这一过程统一为一个连续的、可视的、即时的操作用户滑动滚轮的同时就能看到结果不需要任何确认步骤。2.2 构造函数与核心参数TimePicker 的构造函数接受一个可选对象TimePicker(options?:{selected?:Date})其中selected参数接受一个Date对象用于设置初始选中的时间。注意TimePicker 只关注这个 Date 对象中的小时和分钟部分年月日会被忽略TimePicker({selected:newDate(2026,0,1,8,0)// 初始选中 08:00})在我们的闹钟应用中我们将selectedHour和selectedMinute作为State变量维护并在每次需要构建 TimePicker 时创建一个新的 Date 对象StateselectedHour:number8;StateselectedMinute:number0;TimePicker({selected:newDate(2026,0,1,this.selectedHour,this.selectedMinute)})这里有一个需要特别注意的设计决策TimePicker 的selected参数只在组件初始化时生效——后续修改this.selectedHour或this.selectedMinute不会自动更新 TimePicker 的滚轮位置。这意味着如果你希望通过预设按钮来改变 TimePicker 的显示值单靠修改状态变量是不够的。在我们的实现中预设按钮点击后会同时更新状态变量并直接添加提醒——不依赖 TimePicker 的视觉同步。这在实践中是一个更健壮的模式你的数据流是单向的用户操作 → 更新状态 → 添加提醒不会因为 UI 组件的内部状态而产生不一致。2.3 onChange 回调与数据获取当用户滑动 TimePicker 的滚轮时onChange回调会在选择值变化时触发TimePicker({selected:newDate(2026,0,1,this.selectedHour,this.selectedMinute)}).onChange((value:TimePickerResult){this.onTimeChanged(value);})TimePickerResult包含了两个关键属性onTimeChanged(result:TimePickerResult):void{if(result.hour!undefined){this.selectedHourresult.hour;}if(result.minute!undefined){this.selectedMinuteresult.minute;}}result.hour当前选中的小时数0-2324 小时制result.minute当前选中的分钟数0-59这里我们采用了防御性编程——检查hour和minute是否为 undefined 后才赋值。这是因为TimePickerResult的属性定义中这两个字段是可选的在某些 API 版本中。防御性检查可以避免潜在的类型安全问题。2.4 样式与尺寸控制TimePicker 支持通过链式方法控制外观TimePicker({selected:date}).height(200)// 控制整体高度.backgroundColor(#FFFFFF)// 背景色height是 TimePicker 最重要的样式属性——它决定了滚轮的可视区域大小。过小会导致滚轮难以操作过大则占用过多屏幕空间。对于闹钟选择场景200vp 是一个比较合适的折中值既能清晰显示两组数字小时和分钟又不会过度挤压下方的内容区域。三、TextClock 文本时钟深入解析3.1 组件定位与设计哲学TextClock 是一个特殊的文本显示组件它自动获取系统当前时间以指定的格式实时展示并自动更新显示。与普通的Text组件不同你不需要手动编写setInterval或setTimeout来刷新时间——TextClock 内部已经处理了这一切。这种声明式时钟设计体现了 ArkUI 的核心哲学让框架处理底层复杂性开发者只需描述显示什么。使用 TextClock你只需要告诉它格式它就会忠实地、持续地显示最新时间。3.2 构造函数与选项从 API 定义来看TextClock 的构造函数同样接受一个可选对象TextClock(options?:TextClockOptions)TextClockOptions的属性包括timeZoneOffset?: number时区偏移量取值范围 -14 到 12。负数表示东时区如中国为 -8即 UTC8。不设置时默认使用系统时区。controller?: TextClockController控制器实例用于程序化地停止和启时钟更新。在我们的闹钟应用中使用系统默认时区即可因此不传入任何选项TextClock().format(HH:mm:ss)3.3 format 格式字符串体系format()是 TextClock 最核心的方法——它决定了时间的显示方式。TextClock 使用一套类似于传统日期格式化的模式字符串系统模式含义示例yyyy四位年份2026MM两位月份01-12MMM英文月份缩写Jan-DecMMMM英文月份全称January-Decemberdd两位日期01-31ddd英文星期缩写Mon-Sundddd英文星期全称Monday-SundayHH24 小时制小时00-23hh12 小时制小时01-12mm分钟00-59ss秒00-59默认格式为hh:mm:ss12 小时制带秒。在我们的应用中使用了两种格式// 大号时间显示 — 24 小时制带秒TextClock().format(HH:mm:ss).fontSize(52).fontWeight(FontWeight.Bold).fontColor(#FFFFFF).fontFamily(monospace)// 日期显示 — 中文格式TextClock().format(yyyy年MM月dd日 dddd).fontSize(FontSize.BODY).fontColor(#FFFFFF99)第一行格式HH:mm:ss产生类似14:35:02的输出24 小时制的时间清晰直观。第二行yyyy年MM月dd日 dddd产生类似2026年06月09日 Monday的输出中文日期格式加上英文星期全称兼顾了本地化和语义清晰度。重要提醒format是一个链式方法不是构造参数这一点在初次使用时容易被忽略。如果误写成TextClock({ format: HH:mm })会导致编译错误因为TextClockOptions中不存在format字段。3.4 onDateChange 回调与高级用法除了基本的自动显示TextClock 还提供了onDateChange回调在时间变化时触发TextClock().format(HH:mm:ss).onDateChange((value:number){// value 是 Unix 时间戳毫秒console.log(时间更新: value);})这个回调的最小触发间隔为秒级非表单场景或分钟级表单场景。你可以利用这个回调来做一些时间驱动的逻辑——比如在零点时刷新数据或在特定时刻触发提醒。不过在我们的闹钟应用中TextClock 只作为展示组件使用实际的提醒逻辑由用户操作驱动无需监听时钟回调。3.5 字体与样式定制TextClock 继承了TextClockAttribute类中的所有样式方法包括TextClock().fontSize(52)// 字号52fp.fontWeight(FontWeight.Bold)// 字重加粗.fontColor(#FFFFFF)// 颜色白色.fontFamily(monospace)// 字体等宽.fontStyle(FontStyle.Normal)// 风格正常使用等宽字体monospace是一个经典的时钟设计选择——在等宽字体下每个数字占据相同的宽度当秒数从 09 跳变到 10 时文字宽度不会发生变化避免了因宽度变化带来的视觉抖动。这种微小的细节在长时间的注视下会产生明显的差异。四、实战闹钟提醒应用开发4.1 页面整体架构我们的闹钟提醒页面包含以下几个核心模块当前时间展示区两个 TextClock分别显示时间和日期已启用提醒计数显示当前活跃的提醒数量TimePicker 选择器滚轮式时间选择快捷预设按钮四个预设时间起床/午休/下班/睡觉自定义添加区TextInput 输入名称 添加按钮提醒列表展示所有提醒支持启用/关闭/删除交互流程为用户通过 TimePicker 或快捷预设选择时间 → 添加提醒 → 提醒出现在列表中 → 可随时切换启用状态或删除。4.2 数据模型设计interfaceAlarmItem{id:number;label:string;hour:number;minute:number;enabled:boolean;}interfacePresetItem{label:string;hour:number;minute:number;icon:string;}constPRESETS:PresetItem[][{label:起床,hour:7,minute:0,icon:},{label:午休,hour:12,minute:30,icon:☀️},{label:下班,hour:18,minute:0,icon:},{label:睡觉,hour:22,minute:30,icon:},];AlarmItem是单个提醒的数据模型。除了基本的时间hour/minute和标签label之外还包含一个enabled字段用于控制启用/关闭状态。这个设计让用户可以在不删除提醒的情况下暂停某个提醒——这在真实场景中非常实用比如周末不想被起床闹铃叫醒。PRESETS数组定义了四个快捷预设包含了标签、时间和 emoji 图标。预设的设计逻辑是覆盖一天中的关键时间节点——起床、午休、下班、睡觉——选择了这些时刻最具代表性的时间值。emoji 图标让预设按钮更加直观和有趣。页面的核心状态变量Statealarms:AlarmItem[][];// 提醒列表StateselectedHour:number8;// TimePicker 当前选中的小时StateselectedMinute:number0;// TimePicker 当前选中的分钟StatelabelInput:string;// 提醒名称输入StateshowToast:booleanfalse;// Toast 提示显示4.3 时间格式化工具由于提醒列表中的时间需要以HH:mm格式展示我们实现了一个简单的格式化函数formatTime(h:number,m:number):string{consthh:stringh10?0h.toString():h.toString();constmm:stringm10?0m.toString():m.toString();return${hh}:${mm};}该函数将小时和分钟补零到两位数字确保8:5输出为08:05。之所以不直接使用 Date 对象来格式化是因为单独维护 hour 和 minute 数值更加直观避免了 Date 对象在年月日上的额外信息负担。4.4 添加提醒逻辑addAlarm(label:string,hour:number,minute:number):boolean{// 去重检查for(leti0;ithis.alarms.length;i){if(this.alarms[i].hourhourthis.alarms[i].minuteminute){this.showToastMessage(该时间已有提醒请选择其他时间);returnfalse;}}constalarm:AlarmItem{id:this.nextId,label:label,hour:hour,minute:minute,enabled:true};constnewAlarms:AlarmItem[][alarm];for(leti0;ithis.alarms.length;i){newAlarms.push(this.alarms[i]);}if(newAlarms.length15){newAlarms.splice(15);}this.alarmsnewAlarms;this.labelInput;this.showToastMessage(✅ 提醒添加成功);returntrue;}这个方法的几个设计要点去重检查通过遍历现有提醒检测是否存在相同时间hour 和 minute 都一致的提醒。如果存在拒绝添加并提示用户。这个检查非常重要——在同一个时刻设置两个提醒是没有意义的。新提醒前置通过构造[alarm, ...新数组]的方式将新添加的提醒置顶显示。这与大多数闹钟应用的行为一致——最新添加的提醒应该最容易看到。数量上限最多保留 15 个提醒超出部分自动移除。虽然在实际使用中很少有人会添加超过 15 个闹钟但设置上限是一种防御性编程实践。不可变更新创建全新的数组并赋值给this.alarms而非直接修改原数组。这是 ArkTS 中State变量更新的正确方式——只有整体替换才能触发 UI 刷新。4.5 快捷预设的实现addPresetAlarm(preset:PresetItem):void{this.selectedHourpreset.hour;this.selectedMinutepreset.minute;this.addAlarm(preset.label,preset.hour,preset.minute);}点击预设按钮后先更新 TimePicker 绑定的状态变量虽然可能不会立即反映到滚轮位置上但保持了状态的一致性再直接添加提醒。预设按钮的视觉设计采用了药丸形状 浅蓝底色 emoji 图标的方案Button(${preset.icon}${preset.label}${this.formatTime(preset.hour,preset.minute)}).fontSize(FontSize.CAPTION).fontColor(AppColors.PRIMARY).backgroundColor(AppColors.PRIMARY_LIGHT).borderRadius(BorderRadius.FULL).height(30)每个按钮上同时显示图标、标签和时间信息密度高但阅读负担小。例如 “ 起床 07:00” ——用户一眼就能看到预设的含义和具体时间。4.6 提醒状态切换toggleAlarm(id:number):void{constupdated:AlarmItem[][];for(leti0;ithis.alarms.length;i){if(this.alarms[i].idid){updated.push({id:this.alarms[i].id,label:this.alarms[i].label,hour:this.alarms[i].hour,minute:this.alarms[i].minute,enabled:!this.alarms[i].enabled});}else{updated.push(this.alarms[i]);}}this.alarmsupdated;}切换启用状态时同样遵循不可变更新模式——创建新的数组和修改过的对象。这里使用对象字面量逐个拷贝字段而不是展开运算符因为 ArkTS 对展开运算符的支持有限制。每个提醒卡片上的启用/关闭按钮在视觉上有明显的区分启用状态显示黄色关闭按钮带浅黄背景关闭状态显示绿色开启按钮带浅绿背景。4.7 Toast 提示机制showToastMessage(msg:string):void{this.toastTextmsg;this.showToasttrue;setTimeout((){this.showToastfalse;},1800);}Toast 提示使用setTimeout在 1.8 秒后自动隐藏。Tip 本身是一个绝对定位的深色圆角条显示在屏幕下部 85% 的位置。这种轻量级的反馈机制比弹窗更优雅——不打断用户操作只提供瞬时的信息确认。需要注意的是如果在aboutToDisappear中需要清理定时器应当存储setTimeout的返回值并在组件销毁时clearTimeout。不过由于 Toast 的自动消失时间极短1.8 秒且页面退出时不需要重新渲染这里省略了清理逻辑。五、TimePicker 与 TextClock 的组合模式5.1 信息流向分析在闹钟页面中TimePicker 和 TextClock 之间的信息流向如下系统时钟 ↓ TextClock展示当前时间 ↓用户看到当前时间决定设置何时提醒 TimePicker选择提醒时间 ↓ onChange State selectedHour / selectedMinute ↓ addAlarm() 提醒列表AlarmItem[]TextClock 作为参考时间帮助用户建立时间认知TimePicker 作为输入工具让用户选择目标时间两者通过State变量和提醒列表间接关联。5.2 TimePicker 与 TextInput 的组合除了 TimePicker页面还包含一个 TextInput 用于自定义提醒名称。这是一个典型的选择器 文本输入组合模式Row(){TextInput({placeholder:提醒名称选填如开会,text:this.labelInput}).onChange((value:string){this.labelInputvalue;}).maxLength(20).layoutWeight(1)Button(添加提醒).onClick((){this.addFromPicker();})}这种设计让用户可以选择是否自定义提醒名称——不填则使用默认名称提醒填写则使用自定义名称。TextInput 的限制长度20 字符确保名称不会过长在列表显示中保持美观。5.3 常见的 TimePicker 使用场景除了闹钟提醒TimePicker 还广泛适用于以下场景日程安排结合 DatePicker 设定事件的具体开始时间计时器选择倒计时或正计时的目标时长营业时间设置商户设置每日营业的开始和结束时间免打扰模式设置静默时段的起止时间定时任务如定时发送消息、定时备份数据在每个场景中TimePicker 都承担同样的角色——让用户以直观的方式选择时间点。而 TextClock 则作为当前时间的参考锚点帮助用户做出准确的时间决策。六、完整代码结构页面的组件树结构如下Stack根节点用于 Toast 叠加 └── Column主内容区 ├── Row顶部标题栏⏰ 闹钟提醒 ├── Column时间展示区深色背景 │ ├── TextClock大号 HH:mm:ss │ ├── TextClock日期 yyyy年MM月dd日 dddd │ └── Row已启用提醒计数 ├── RowTimePicker 时间选择器 ├── Row快捷预设按钮 ├── Row自定义名称输入 添加按钮 └── 提醒列表 / 空状态 ├── [有提醒] List ForEach 提醒卡片 └── [无提醒] 空状态提示代码约 280 行所有组件和逻辑在一个文件中。使用了 TimePicker、TextClock、TextInput、Button、List、ForEach 等 ArkUI 组件。七、总结本文以闹钟提醒为业务场景深入解析了 ArkUI 的两个时间相关组件TimePicker时间选择器和 TextClock文本时钟。回顾本文覆盖的核心要点TimePicker 核心机制通过selected参数设置初始时间通过onChange回调获取用户选择的小时和分钟。TimePicker 是一个滚轮式拾取器高度可由height()控制。TextClock 自动更新无需手动编写定时器TextClock 自动与系统时钟同步。通过format()链式方法设置显示格式支持 yyyy、MM、dd、HH、hh、mm、ss、ddd、dddd 等多种模式。format 链式方法特别注意format是 TextClock 的方法而非构造参数。错误地写在构造函数中会导致编译失败。组合使用模式TextClock 展示现在TimePicker 选择将来两者通过 State 变量和提醒列表间接关联。这是时间相关 UI 的经典信息架构。快捷预设设计通过预设数组起床/午休/下班/睡觉减少用户操作步骤每个按钮同时展示图标、标签和时间。状态切换提醒的启用/关闭状态通过创建新对象和不可变数组更新实现确保 State 机制正常触发 UI 刷新。TimePicker 和 TextClock 虽然各司其职但在实际应用中几乎是形影不离的组合。掌握好这两个组件你就能够自如地处理各种时间相关的交互需求——从简单的当前时间展示到复杂的闹钟提醒管理再到日程安排和定时任务。