鸿蒙自定义时间选择器开发
鸿蒙自定义时间选择器实现指南 本文详细介绍了在鸿蒙系统中开发自定义时间选择器组件的完整流程。通过流程图展示了从初始化数据到UI构建的完整逻辑链,包含以下关键点: 组件采用双时间选择模式(开始/结束时间),通过Swiper滑动视图切换 时间选择分为三个Tab页(今天/明天/后天),使用垂直布局的Tabs组件 核心功能包括: 时间验证逻辑(结束时间必须晚于开始时间) 动态时间更新机制 响应式UI状态管
鸿蒙自定义组件(自定义时间选择器)
文章目录
前言
鸿蒙官方提供了大量组件,使用起来很方便,但是在开发时为了统一样式自定义样式是必不可少的,今天在开发时遇到了需要自定义组件的时间选择器,因为博主使用自定义组件比较少所以不太熟练,在查阅官方文档中废了不少时间,所有编写了这个博客来记录一下如何使用和使用中踩到的坑,希望可以帮助到你
实现效果图:

1.自定义弹窗逻辑梳理
流程图讲解
一、初始化阶段:组件启动的基础准备
流程起点为 进入 TimeRangeDialog 组件,首先执行 数据与控制器初始化(init_data),这是组件功能正常运行的前提,具体包含 4 个关键子步骤:
- 控制器初始化(init_controller):创建
SwiperController,用于后续控制“开始时间/结束时间”滑动视图的切换(如showNext()、showPrevious()方法)。 - 状态变量初始化(init_state):定义两个核心状态变量:
currentIndex:默认值 0,用于标记 Tabs 组件(日期选择)当前选中的索引(0 = 今天、1 = 明天、2 = 后天)。currentnuber:默认值 0,用于标记 Swiper 组件(时间视图)当前显示的页面(0 = 开始时间视图、1 = 结束时间视图)。
- 时间状态初始化(init_time):预设 4 个时间变量,为用户提供合理初始值:
startTime:默认当前时间,记录用户最终选择的开始时间。endTime:默认当前时间 + 1 小时,记录用户最终选择的结束时间。oneTomorrow:当前时间 + 1 天,对应“明天” Tab 的日期。dayAfterTomorrow:当前时间 + 2 天,对应“后天” Tab 的日期(显示为具体“X 月 X 日”文本)。
- 回调初始化(init_callback):定义
confirm回调函数,用于将用户最终选择的startTime和endTime传递给父组件,实现数据回传。
二、UI 构建阶段:分层搭建交互界面
初始化完成后进入 整体 UI 构建(build_ui),采用鸿蒙常用的 Column 纵向布局,将界面分为“标题栏”“时间选择区域”“底部按钮区” 3 个核心模块:
1. 标题栏模块(build_title):基础导航与关闭功能
- 标题显示(show_title_text):展示“选择预约时段”文本,配置字体大小 18、中等粗细、颜色 #18181B,并居中对齐。
- 关闭按钮(build_close_btn):使用透明背景按钮包裹“删除图标”(
homedelete),点击后调用controller.close()直接关闭对话框(close_dialog)。
2. 时间选择区域(build_time_area):核心交互区,分 3 层设计
该区域通过“按钮切换 + 滑动视图 + 日期 Tabs + 时间选择器”实现功能:
(1)顶层:时间视图切换按钮(build_time_btn_row)
采用 Row 均匀布局,包含 3 个元素:
- 开始时间按钮(build_start_btn):显示“开始时间”标签(蓝色背景、圆角样式)和当前
startTime(如“3 月 20 日 14:30”),点击后触发btn_click_start:将currentnuber设为 0,并调用swiperController.showNext()切换到开始时间视图。 - 分隔箭头(show_arrow):使用“右半箭头图标”(
homeRight_half_arrow)分隔两个按钮。 - 结束时间按钮(build_end_btn):显示
endTime,点击后触发btn_click_end:将currentnuber设为 1,调用swiperController.showPrevious()切换到结束时间视图。
(2)中层:Swiper 滑动视图(build_swiper)
绑定初始化的 SwiperController:
- 滑动页内容:两个页面均通过
timeSelection()方法构建(参数id用于区分开始/结束)。 - 滑动响应(swiper_change):用户手动滑动时触发
onChange事件,更新currentnuber为当前索引。 - 指示器(set_swiper_indicator):使用
DotIndicator显示位置(选中/未选中状态均为白色)。
滑动效果:
(3)底层:日期 Tabs 与时间选择器(build_time_selection)
通过 Tabs 纵向布局(vertical=true)实现:
-
Tabs 基础配置(tabs_vertical):设置 Tab 栏在左侧(
barPosition=Start)、高度 250、宽度 80、背景色 #f0f3f8。 -
3 个日期 Tab:
tab_today(今天):文本为“今天”,内容调用timeSelectionutensil()构建时间选择器。tab_tomorrow(明天):文本为“明天”,内容同上。tab_day_after(后天):文本为dayAfterTomorrow的“X 月 X 日”格式,内容同上。
-
Tab 样式(build_tab_name):根据
currentIndex切换背景色(选中为 #ffffff,未选中为透明)。 -
Tab 切换响应(tabs_change):用户点击 Tab 时触发
onChange事件,更新currentIndex,并进入update_time_by_tab逻辑:- 若
currentIndex=0(今天):调用copyDateTime(startDate, 布尔值),赋值给startTime或endTime(基于currentnuber)。 - 若
currentIndex=1(明天):使用oneTomorrow赋值。 - 若
currentIndex=2(后天):使用dayAfterTomorrow赋值。
- 若
-
copyDateTime 方法(copy_date_time):复制源日期的年、月、日、时、分到目标时间,确保时间修改独立性。
(4)时间选择器(build_time_picker)
每个 Tab 内容区通过 timeSelectionutensil() 构建:
- 基础配置(time_picker_config):默认选中时间为
startDate(当前时间),启用 24 小时制(useMilitaryTime=true)。 - 选择响应(time_picker_change):用户调整时间时触发
onChange事件:currentnuber=0:更新startTime的小时(setHours)和分钟(setMinutes)。currentnuber=1:更新endTime的小时和分钟。
3. 底部按钮区(build_bottom_btn):确认选择与时间验证
采用 Row 布局:
- 按钮构建(build_confirm_btn):复用自定义组件
black_button,设置宽度 100%、文本“确定”。 - 点击响应(btn_click_confirm):点击后执行
validateTimeRange()验证:- 验证失败:若
startTime时间戳 ≥endTime时间戳,调用promptAction.showToast()弹出提示“结束时间必须晚于开始时间”(时长 2000ms)。 - 验证成功:调用
confirm回调函数传递数据,随后调用controller.close()关闭对话框(close_after_confirm)。
验证成功:
验证失败:
- 验证失败:若
三、核心逻辑总结:状态同步与交互闭环
整个流程的核心是“状态变量驱动视图,视图交互更新状态”,关键闭环包括:
1.视图切换闭环:currentnuber 关联 Swiper 视图,通过按钮点击或手动滑动更新状态。
2. 日期选择闭环:currentIndex 关联 Tabs 选中项,切换时通过 copyDateTime 同步更新日期。
3. 时间验证闭环:确认前强制验证时间范围,失败时提示修正,成功后才回传数据并关闭组件。 1. 初始化 TimeRangeDialog
四、TimeRangeDialog(时间范围选择弹窗)使用步骤
一、前置准备:确认依赖与环境
在使用组件前,需确保项目环境和依赖满足以下条件,避免出现引用错误:
- API 版本适配:组件基于鸿蒙 ArkTS 开发,需确保项目的
minSdkVersion支持@ohos.promptAction(弹窗提示)、CustomDialog(自定义弹窗)、Swiper(滑动容器)等组件,建议使用 API Version 9 及以上。 - 依赖组件引入:
- 组件依赖
@ohos.promptAction(用于时间校验失败的 Toast 提示),无需额外安装,直接在页面头部导入即可。 - 组件依赖自定义按钮组件
black_button(路径为./ButtonuUI),需确保项目中存在该组件文件,且组件接收width1(宽度)和text(按钮文本)两个参数。
- 组件依赖
- 资源文件确认:组件中引用了两张图片资源(
app.media.homedelete关闭图标、app.media.homeRight_half_arrow箭头图标),需在项目的main_pages.json对应的资源目录中添加这两张图片,或替换为项目中已有的同类资源。
二、步骤1:导入 TimeRangeDialog 组件
在需要使用“时间范围选择弹窗”的页面(如预约页面)中,首先导入 TimeRangeDialog 组件和 promptAction(若页面未导入),代码如下:
// 导入弹窗提示工具(用于时间校验失败提示)
import promptAction from '@ohos.promptAction';
// 导入自定义时间范围弹窗组件(路径需与组件实际存放路径一致)
import { TimeRangeDialog } from './TimeRangeDialog'; // 假设 TimeRangeDialog 存放在当前页面同级目录
三、步骤2:创建 CustomDialogController 实例
通过 CustomDialogController 控制 TimeRangeDialog 的显示/隐藏、弹窗样式(位置、背景、圆角等),并绑定“确认选择”后的回调函数。
核心代码示例:
在页面的结构体(struct)中定义 dialogController 变量,作为弹窗的控制器:
@Entry
@Component
struct ReservationPage { // 示例:预约页面
// 1. 定义弹窗控制器,配置弹窗样式与回调
dialogController: CustomDialogController = new CustomDialogController({
// 绑定 TimeRangeDialog 组件,传入 confirm 回调(核心:接收选择的开始/结束时间)
builder: TimeRangeDialog({
confirm: (startTime: Date, endTime: Date) => {
// 这里是“用户点击确定按钮后”的逻辑,startTime 和 endTime 为用户选择的时间
this.handleConfirm(startTime, endTime);
}
}),
// 弹窗位置:底部弹出(符合常见选择器交互)
alignment: DialogAlignment.Bottom,
// 是否允许点击弹窗外部关闭:false(需用户主动点击关闭按钮或确定按钮)
autoCancel: false,
// 弹窗背景蒙版颜色:半透明黑色(遮罩页面,突出弹窗)
maskColor: 'rgba(0, 0, 0, 0.3)',
// 弹窗宽度:占满屏幕宽度
width: '100%',
// 弹窗圆角:仅顶部左右圆角(底部直角,贴合底部弹窗样式)
cornerRadius: {
topLeft: 10,
topRight: 10,
bottomLeft: 0,
bottomRight: 0
}
});
// 2. 页面构建(示例:添加“打开时间选择弹窗”的按钮)
build() {
Column({ space: 20 }) {
// 触发弹窗的按钮(可根据实际页面样式调整)
Button('选择预约时间')
.width(200)
.height(45)
.backgroundColor('#3092f3')
.fontColor(Color.White)
.borderRadius(8)
.onClick(() => {
// 点击按钮,打开时间选择弹窗
this.dialogController.open();
});
// 其他页面内容...
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
// 3. 定义“确认选择”的回调处理函数(用户点击弹窗“确定”后执行)
private handleConfirm(startTime: Date, endTime: Date) {
// 示例1:打印选择的时间(便于调试)
console.log(`用户选择的开始时间:${startTime.toLocaleString()}`);
console.log(`用户选择的结束时间:${endTime.toLocaleString()}`);
// 示例2:后续业务逻辑(如调用“预约电桩”接口)
// this.callReservationApi(startTime, endTime); // 需自行实现接口调用逻辑
}
}
四、步骤3:使用弹窗进行时间选择(用户操作流程)
用户点击“选择预约时间”按钮后,弹窗会从页面底部弹出,用户可按以下流程完成时间选择:
1. 切换“开始时间/结束时间”选择页
弹窗中部通过 Swiper(滑动容器) 分为两个页面:
- 左侧:开始时间选择页(默认显示)
- 右侧:结束时间选择页
- 切换方式:
- 点击弹窗中部的“开始时间”或“结束时间”按钮,自动滑动到对应页面;
- 直接用手指左右滑动弹窗中部区域,切换页面。
2. 选择日期(今天/明天/后天)
弹窗时间选择区顶部有 Tabs(标签页),提供3个日期选项:
- Tab1:今天(默认选中)
- Tab2:明天
- Tab3:后天(显示为“X月X日”格式,自动计算当前日期后2天)
- 操作:点击对应 Tab,即可切换到目标日期(切换后时间选择器会同步更新为该日期)。
3. 选择具体时间(小时/分钟)
日期切换后,下方会显示 TimePicker(时间选择器),支持:
- 24小时制(组件已通过
useMilitaryTime(true)开启); - 滑动选择“小时”和“分钟”(选择后实时更新弹窗中部的时间显示)。
4. 确认/取消选择
- 确认选择:点击弹窗底部的“确定”按钮,组件会先校验时间有效性(结束时间必须晚于开始时间):
- 校验通过:关闭弹窗,并通过
confirm回调将选择的startTime(开始时间)和endTime(结束时间)传递给页面; - 校验失败:不关闭弹窗,弹出 Toast 提示“结束时间必须晚于开始时间”。
- 校验通过:关闭弹窗,并通过
- 取消选择:点击弹窗顶部右侧的“关闭图标”(
homedelete图片),直接关闭弹窗,不触发任何回调。
五、关键注意事项
- 时间格式处理:
confirm回调返回的startTime和endTime是Date类型,若需传递给接口(如转为“yyyy-MM-dd HH:mm”字符串),需自行处理格式,示例代码:private formatDate(date: Date): string { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, '0'); // 月份从0开始,补0为两位数 const day = String(date.getDate()).padStart(2, '0'); const hour = String(date.getHours()).padStart(2, '0'); const minute = String(date.getMinutes()).padStart(2, '0'); return `${year}-${month}-${day} ${hour}:${minute}`; } - 默认时间配置:组件默认“结束时间为当前时间+1小时”,若需修改默认时间(如默认结束时间为开始时间+2小时),可调整
TimeRangeDialog中endDate的初始化代码:// 原代码:默认结束时间=当前时间+1小时 @State endDate: Date = new Date(Date.now() + 3600000); // 修改为:默认结束时间=当前时间+2小时(7200000毫秒) @State endDate: Date = new Date(Date.now() + 7200000); - 样式自定义:若需修改弹窗颜色、字体大小、按钮样式等,可直接修改
TimeRangeDialog组件内部的样式代码(如标题字体颜色#18181B、按钮背景色#3092f3等)。
源码获取
gitee源码拉取下来即可使用
更多推荐



所有评论(0)