【HarmonyOS 6】HarmonyOS 自定义时间选择器实现

📅 2026/7/22 23:20:14
【HarmonyOS 6】HarmonyOS 自定义时间选择器实现
前言在开发时间管理类应用时时间选择器是一个非常常见的功能。本文将通过近期在鸿蒙应用开发中的一个实际案例详细讲解如何在 HarmonyOS 应用中实现一个自定义的时间选择器。我们这个案例中的选择器支持半小时为单位的时间选择适合用于记录时间块等场景。应用场景在本应用中用户需要记录已经完成的时间块。当用户点击记录时间按钮时会弹出一个对话框让用户选择开始时间和结束时间。注时间选择器支持小时和分钟的独立选择分钟只能选择 00 或 30核心知识点1. CustomDialog 自定义对话框在 HarmonyOS 中CustomDialog装饰器用于创建自定义对话框。与系统提供的标准对话框不同自定义对话框可以完全控制布局和交互逻辑。CustomDialogstruct TimePickerDialogContent{controller:CustomDialogController// 对话框控制器// ... 其他属性和方法}2. CustomDialogController 对话框控制器CustomDialogController用于控制对话框的显示和关闭。在父组件中创建控制器实例然后调用open()方法显示对话框。privatetimePickerController:CustomDialogController|nullnull// 创建并打开对话框this.timePickerControllernewCustomDialogController({builder:TimePickerDialogContent({/* 参数 */}),autoCancel:true,alignment:DialogAlignment.Center})this.timePickerController.open()builder自定义弹窗内容构造器。autoCancel是否允许点击遮障层退出true表示关闭弹窗。false表示不关闭弹窗。alignment弹窗在竖直方向上的对齐方式。3. State 状态管理State装饰器用于声明组件的状态变量。当状态变量的值发生变化时UI 会自动更新。StatetempHour:number0// 临时存储选中的小时StatetempMinute:number0// 临时存储选中的分钟完整实现第一步定义对话框结构首先我们创建一个自定义对话框组件定义它需要的属性CustomDialogstruct TimePickerDialogContent{controller:CustomDialogController// 对话框控制器必需selectedTime:DatenewDate()// 当前选中的时间onConfirm:(hour:number,minute:number)void(){}// 确认回调StatetempHour:number0// 临时小时值StatetempMinute:number0// 临时分钟值// 小时选项0-23privatehours:string[][00,01,02,03,04,05,06,07,08,09,10,11,12,13,14,15,16,17,18,19,20,21,22,23]// 分钟选项只有 00 和 30privateminutes:string[][00,30]// ... 后续代码}这边我们写的已经很清楚了根据我们的需求小时的选项是完整的从0-23但是分钟的选项只有00和30这是根据我们的需求来设定的。第二步初始化时间值在对话框显示之前我们需要将传入的selectedTime转换为小时和分钟并调整到最近的半小时aboutToAppear():void{// 获取小时0-23this.tempHourthis.selectedTime.getHours()// 将分钟调整为最近的半小时constminutesthis.selectedTime.getMinutes()if(minutes15){// 0-14分钟 → 向下取整到 00this.tempMinute0}elseif(minutes45){// 15-44分钟 → 向上取整到 30this.tempMinute30}else{// 45-59分钟 → 向上取整到下一个小时的 00this.tempHour(this.tempHour1)%24// % 24 确保不超过 23this.tempMinute0}}代码说明aboutToAppear()是组件生命周期方法在组件即将显示时调用我们将任意分钟值调整为 00 或 30这样用户看到的初始值就是半小时对齐的使用% 24确保小时值在 0-23 范围内例如 23 点 50 分会变成 0 点 00 分第三步构建 UI 布局接下来我们使用build()方法构建对话框的 UIbuild(){Column(){// 标题Text(选择时间).fontSize(18).fontWeight(FontWeight.Bold).fontColor($r(app.color.text_primary)).margin({bottom:12})// 提示文字Text(仅支持半小时为单位).fontSize(12).fontColor($r(app.color.text_secondary)).margin({bottom:16})// 时间选择器区域Row(){// 小时选择器Column(){Text(小时).fontSize(12).fontColor($r(app.color.text_secondary)).margin({bottom:8})TextPicker({range:this.hours,// 选项数组selected:this.tempHour// 默认选中项的索引}).onChange((value:string|string[],index:number|number[]){// 当用户滑动选择器时触发if(typeofindexnumber){this.tempHourindex// 更新小时值}})}.layoutWeight(1)// 占据一半宽度// 分隔符Text(:).fontSize(24).fontWeight(FontWeight.Bold).fontColor($r(app.color.text_primary)).margin({left:12,right:12})// 分钟选择器Column(){Text(分钟).fontSize(12).fontColor($r(app.color.text_secondary)).margin({bottom:8})TextPicker({range:this.minutes,selected:this.tempMinute0?0:1// 0分钟→索引030分钟→索引1}).onChange((value:string|string[],index:number|number[]){if(typeofindexnumber){// 将索引转换为实际分钟值this.tempMinuteindex0?0:30}})}.layoutWeight(1)}.width(100%).margin({bottom:16})// 显示当前选中的时间Text(已选择:${this.tempHour.toString().padStart(2,0)}:${this.tempMinute.toString().padStart(2,0)}).fontSize(16).fontWeight(FontWeight.Bold).fontColor($r(app.color.primary_color)).margin({bottom:16})// 按钮区域Row(){// 取消按钮Button(取消).fontSize(15).fontColor($r(app.color.text_secondary)).backgroundColor($r(app.color.input_background)).borderRadius(22).layoutWeight(1).height(44).onClick((){this.controller.close()// 关闭对话框不返回任何值})// 确定按钮Button(确定).fontSize(15).fontColor(Color.White).backgroundColor($r(app.color.primary_color)).borderRadius(22).layoutWeight(1).height(44).margin({left:10}).onClick((){// 调用回调函数将选中的时间传回父组件this.onConfirm(this.tempHour,this.tempMinute)this.controller.close()// 关闭对话框})}.width(100%)}.width(75%)// 对话框宽度为屏幕的 75%.padding(20).backgroundColor($r(app.color.card_background))// 使用主题颜色.borderRadius(16)// 圆角}第四步在父组件中使用现在我们在父组件中创建并打开这个时间选择器Componentexportstruct AddTimeBlockDialog{StatestartTime:DatenewDate()// 开始时间StateendTime:DatenewDate()// 结束时间privatestartTimePickerController:CustomDialogController|nullnull// 格式化时间显示例如14:30privateformatTime(date:Date):string{consthoursdate.getHours().toString().padStart(2,0)constminutesdate.getMinutes().toString().padStart(2,0)return${hours}:${minutes}}// 打开开始时间选择器privateopenStartTimePicker():void{this.startTimePickerControllernewCustomDialogController({builder:TimePickerDialogContent({selectedTime:this.startTime,// 传入当前时间onConfirm:(hour:number,minute:number){// 用户点击确定后更新开始时间constnewTimenewDate(this.startTime)newTime.setHours(hour)newTime.setMinutes(minute)newTime.setSeconds(0)newTime.setMilliseconds(0)this.startTimenewTime// 更新状态UI 会自动刷新}}),autoCancel:true,// 点击对话框外部自动关闭alignment:DialogAlignment.Center,// 居中显示customStyle:true// 使用自定义样式})this.startTimePickerController.open()// 显示对话框}build(){Column(){// 开始时间按钮Button(){Column(){Text(开始).fontSize(11).fontColor($r(app.color.text_secondary))Text(this.formatTime(this.startTime)).fontSize(16).fontWeight(FontWeight.Medium).fontColor($r(app.color.text_primary)).margin({top:2})}}.backgroundColor($r(app.color.input_background)).borderRadius(10).padding(12).onClick((){this.openStartTimePicker()// 点击按钮打开选择器})}}}这边来具体讲一下相关的配置CustomDialogController 配置builder指定对话框的内容组件autoCancel是否允许点击外部关闭alignment对话框在屏幕上的位置customStyle是否使用自定义样式如果为 false会使用系统默认样式回调函数onConfirm是一个箭头函数当用户点击确定时被调用我们在回调中更新startTime状态UI 会自动刷新显示新时间Date 对象操作创建新的 Date 对象new Date(this.startTime)设置小时和分钟setHours()和setMinutes()清零秒和毫秒确保时间精确到分钟总结通过本教程我们学习了如何在 HarmonyOS 中实现一个自定义时间选择器。也通过一个案例知道了如何在实践中实现。这个时间选择器可以直接应用到你的项目中也可以根据需求进行扩展和定制。希望这篇教程对你有所帮助参考资料HarmonyOS 官方文档 - CustomDialogHarmonyOS 官方文档 - TextPicker