鸿蒙新特性——DatePicker 日期选择器组件详解
一、引言
日期选择是移动端应用中最基础也最频繁的操作之一。订机票时选择出发日期和返程日期,酒店预订中选择入住和退房日期,日程管理中选择会议时间——日期选择无处不在。在传统开发中,实现一个日期选择器需要处理日历算法(每月天数、闰年判断)、滚动交互、日期格式化、范围校验等复杂逻辑,工作量极大且容易出错。
HarmonyOS 提供了 DatePicker 组件——一个内置完整日历逻辑的日期选择器。它自动处理月份天数、闰年、日期范围等复杂计算,支持公历/农历切换,通过声明式 API 即可完成日期选择的所有功能。开发者只需指定起始日期、结束日期和默认选中日期,DatePicker 负责剩下的所有交互细节。
本文通过一个出行日期选择 Demo 深入讲解 DatePicker 组件的核心用法:如何设置日期范围?如何联动出发和返回日期?如何切换农历显示?以及 onDateChange 回调的使用模式。
阅读完本文,你将能够:
- 使用 DatePicker 替代自定义日历选择方案
- 掌握
start/end/selected构造函数参数 - 使用
lunar()链式方法切换农历显示 - 实现双 DatePicker 联动(出发日期 → 返回日期)
- 理解
onDateChange回调与日期校验的最佳实践
二、DatePicker 组件 API 总览
2.1 构造函数
DatePicker(options?: DatePickerOptions)
DatePickerOptions 包含三个可选参数:
interface DatePickerOptions {
start?: Date; // 起始日期,默认 1970-1-1
end?: Date; // 结束日期,默认 2100-12-31
selected?: Date; // 默认选中日期,默认系统当前日期
}
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
start |
Date | 1970-01-01 | 可选择的最早日期 |
end |
Date | 2100-12-31 | 可选择的最晚日期 |
selected |
Date | 当前系统日期 | 初始选中的日期 |
2.2 链式方法
// 农历显示
.lunar(value: boolean): DatePickerAttribute
// 日期变化回调
.onDateChange(callback: Callback<Date>): DatePickerAttribute
| 方法 | 说明 |
|---|---|
lunar(boolean) |
是否显示农历日历 |
onDateChange(Callback<Date>) |
用户选择新日期时的回调,参数为 Date 对象 |
注意:onChange 已在 API 10 废弃,应使用 onDateChange。onDateChange 的回调参数是 Date 对象而非 DatePickerResult,可以直接用于日期比较和运算。
2.3 DatePicker vs 自定义日历
| 特性 | DatePicker | 自定义日历 |
|---|---|---|
| 日历算法 | 内置(月份天数、闰年) | 需手动实现 |
| 滚动交互 | 内置(滚轮式选择) | 需手动实现 |
| 农历支持 | 一行 .lunar(true) |
需引入农历库 |
| 日期范围限制 | start/end 参数 |
需手动校验 |
| 样式定制 | 有限(textStyle/selectedTextStyle) | 完全自由 |
| 双日期联动 | 需手动处理 onDateChange |
同左 |
DatePicker 适合标准日期选择场景(出行、预订、日程),自定义日历适合需要高度定制 UI 的场景(如日历视图、日期标记)。
三、Demo 设计:出行日期选择
3.1 功能概述
Demo 是一个出行日期选择页面,模拟机票/酒店预订的日期选择流程:
- 出发日期选择:DatePicker 选择出发日期,范围从今天起一年内
- 返回日期选择:DatePicker 选择返回日期,start 自动跟随出发日期
- 智能联动:选择出发日期时,如果返回日期早于出发日期,自动调整为出发日期+1天
- 农历切换:Toggle 开关控制两个 DatePicker 同时切换农历/公历显示
- 行程信息:实时显示出发/返回日期、星期、行程天数
- 日期详情表:信息汇总卡片展示当前所有日期相关参数
3.2 出发日期选择器
DatePicker({
start: new Date(), // 从今天开始
end: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 到一年后
selected: this.departDate // 初始为今天
})
.lunar(this.showLunar) // 农历开关跟随 @State
.onDateChange((value: Date) => {
this.departDate = value;
if (value.getTime() >= this.returnDate.getTime()) {
this.returnDate = new Date(value.getTime() + 24 * 60 * 60 * 1000);
}
this.updateTripDays();
})
关键逻辑:
start设置为new Date()确保用户无法选择过去的日期end设置为一年后,控制可选范围onDateChange回调中检查:如果选择的出发日期 >= 返回日期,自动将返回日期设为出发日期+1天- 这种"智能联动"是出行日期选择的标配体验——确保不会出现"返回早于出发"的无效状态
3.3 返回日期选择器
DatePicker({
start: new Date(this.departDate.getTime() + 24 * 60 * 60 * 1000), // 出发日期+1天
end: new Date(Date.now() + 365 * 24 * 60 * 60 * 1000), // 到一年后
selected: this.returnDate
})
.lunar(this.showLunar)
.onDateChange((value: Date) => {
this.returnDate = value;
this.updateTripDays();
})
返回日期的 start 动态绑定到 this.departDate.getTime() + 1天——当用户修改出发日期后,返回日期选择器的起始日期自动变化。这确保了用户在选择返回日期时,不可能选到早于出发日期的日期。DatePicker 的 start 参数在运行时重新计算,每次 @State departDate 变化后,返回选择器的 start 也跟随更新。
这种"响应式日期范围"的设计比静态校验更优雅——用户根本看不到不合法的日期选项,而非选中后报错。
3.4 农历切换
@State showLunar: boolean = false;
// 在 Toggle 中切换
Toggle({ type: ToggleType.Switch, isOn: this.showLunar })
.selectedColor('#1677FF')
.onChange((v: boolean) => { this.showLunar = v; })
// 两个 DatePicker 都绑定到同一 @State
DatePicker({ ... })
.lunar(this.showLunar) // 出发选择器
DatePicker({ ... })
.lunar(this.showLunar) // 返回选择器
lunar() 接受一个 boolean 值。当 showLunar 变化时,两个 DatePicker 同步切换农历/公历显示。农历模式在日期滚轮的每个公历日期下方显示对应的农历日期,对于国内用户查看传统节日(春节、中秋等)非常实用。
3.5 行程天数计算
updateTripDays(): void {
const diff = this.returnDate.getTime() - this.departDate.getTime();
this.tripDays = Math.round(diff / (24 * 60 * 60 * 1000));
}
通过两个 Date 对象的时间戳差值计算天数。getTime() 返回毫秒数,除以一天的毫秒数(24×60×60×1000)得到天数。Math.round 处理浮点精度问题。
3.6 页面结构
┌──────────────────────────────────────────┐
│ 📅 日期选择(深色标题栏) │
├──────────────────────────────────────────┤
│ 📘 DatePicker 组件说明卡片 │
├──────────────────────────────────────────┤
│ ┌────────────────────────────────────┐ │
│ │ 2026-06-26 → 2026-07-03 │ │
│ │ 周五 7天 周四 │ │
│ └────────────────────────────────────┘ │
├──────────────────────────────────────────┤
│ 出发日期 2026-06-26 周五 │
│ ┌────────────────────────────────────┐ │
│ │ [DatePicker] │ │
│ │ 年 月 日 │ │
│ └────────────────────────────────────┘ │
├──────────────────────────────────────────┤
│ 返回日期 2026-07-03 周四 │
│ ┌────────────────────────────────────┐ │
│ │ [DatePicker] │ │
│ │ 年 月 日 │ │
│ └────────────────────────────────────┘ │
├──────────────────────────────────────────┤
│ 农历显示 [====] │
├──────────────────────────────────────────┤
│ 日期详情 │
│ ┌────────────────────────────────────┐ │
│ │ 出发日期 2026-06-26 │ │
│ │ 出发星期 周五 │ │
│ │ 返回日期 2026-07-03 │ │
│ │ 返回星期 周四 │ │
│ │ 行程天数 7 天 │ │
│ │ 农历模式 关闭 │ │
│ └────────────────────────────────────┘ │
└──────────────────────────────────────────┘

四、DatePicker 组件的最佳实践
4.1 日期范围的设计
日期范围的设置决定了用户体验的质量:
- start 不应硬编码:Demo 中出发选择器的 start 为
new Date()(今天),返回选择器的 start 为出发日期+1天。硬编码的日期(如new Date('2026-01-01'))会导致 Demo 过期后无法使用。 - end 要有合理的边界:Demo 中选择一年后作为 end。对于酒店预订(通常最多预订 6 个月),end 可以设得更近——减少用户的滚动量。
- 动态 start:返回选择器的 start 绑定到出发日期,当出发日期变化时,返回的起始日期自动更新。这是 DatePicker 的响应式能力——
start参数在每次渲染时重新计算。
4.2 onDateChange 回调的使用模式
onDateChange 在用户每次滚动选择日期时触发。回调中应处理三件事:
- 更新 @State:将新日期赋值给状态变量,触发 UI 刷新
- 联动校验:如果存在多个 DatePicker 的依赖关系(如出发→返回),在回调中检查和修正
- 计算衍生数据:如行程天数、工作日判断等
.onDateChange((value: Date) => {
// 1. 更新状态
this.departDate = value;
// 2. 联动校验
if (value.getTime() >= this.returnDate.getTime()) {
this.returnDate = new Date(value.getTime() + 24 * 60 * 60 * 1000);
}
// 3. 计算衍生数据
this.updateTripDays();
})
4.3 农历模式的使用场景
农历模式通过 .lunar(true) 开启。适合以下场景:
- 传统文化相关应用:需要显示农历日期、节气、传统节日
- 出行日期选择:用户可能根据农历选择出行日期(如避开清明节、选择中秋节)
- 国内用户群体:大多数中国用户对农历日期有认知需求
农历模式会增加 DatePicker 的显示密度——每个公历日期下方多一行农历文字。在小屏幕设备上需要考虑阅读舒适度。
4.4 双 DatePicker 联动模式
Demo 中的出发/返回双日期选择是一种经典的双 DatePicker 联动模式。其核心逻辑链为:
用户修改出发日期
→ onDateChange 更新 departDate
→ 检查 returnDate >= departDate(如否,自动调整)
→ 返回选择器的 start 变为 departDate + 1 天
→ 更新行程天数
这种联动通过 @State 的响应式更新实现了自动化的日期范围管理。开发者不需要手动调用 DatePicker 的"更新方法"——DatePicker 从 @State 变量中读取新的 start/end/selected 值,在下次渲染时自动应用。
4.5 DatePicker 与其他日期组件的配合
DatePicker 通常与以下组件配合使用:
- TextClock:同时展示"当前选择的日期"和"实时时钟"
- Text:显示格式化的日期字符串和星期
- Toggle:控制农历开关
- Button:确认选择、提交预约等操作
在 Demo 中,DatePicker 的选中日期通过 Text 展示在顶部摘要区域和底部详情表中,让用户可以清楚地看到当前选择,而非仅依赖 DatePicker 滚轮中的选中状态。
五、完整代码结构
DatePickerPage (~240 行)
├── 状态变量
│ ├── @State departDate — 出发日期
│ ├── @State returnDate — 返回日期(初始出发+7天)
│ ├── @State showLunar — 农历显示开关
│ └── @State tripDays — 行程天数
├── 方法
│ ├── updateTripDays() — 计算行程天数
│ ├── formatDate(d) — 日期格式化为 yyyy-MM-dd
│ └── getWeekday(d) — 获取星期中文名
├── 视图
│ ├── 标题栏 — 📅 日期选择
│ ├── 说明卡片 — DatePicker 组件介绍
│ ├── 行程摘要 — 出发/→/返回 + 天数
│ ├── 出发日期选择器 — DatePicker + 标题 + 已选日期
│ ├── 返回日期选择器 — DatePicker(start 联动)+ 标题 + 已选日期
│ ├── 农历 Toggle — Switch 控制
│ └── 日期详情表 — 6 行信息汇总
└── @Builder infoRow() — 详情行组件
六、总结
本文通过一个出行日期选择 Demo 深入讲解了 HarmonyOS 中的 DatePicker 日期选择器组件。DatePicker 将日历算法、滚动交互、范围管理封装为声明式组件,通过 start/end/selected 三个构造函数参数和 lunar()/onDateChange() 两个链式方法,覆盖了日期选择的绝大部分需求。
核心要点回顾:
-
三个构造函数参数控制范围:
start指定最早可选日期,end指定最晚可选日期,selected指定初始选中日期。参数支持动态值,可在 @State 变化时自动更新。 -
onDateChange 替代废弃的 onChange:回调参数是
Date对象(非DatePickerResult),可直接使用getTime()、getDay()等方法进行日期比较和计算。 -
lunar() 切换农历:一行代码实现公历/农历切换。农历模式在日期下方显示对应农历日期,适合国内用户和传统文化场景。
-
双 DatePicker 联动:通过 @State 变量建立依赖关系——返回选择器的
start绑定到出发日期+1天,出发选择器的onDateChange中自动修正返回日期。响应式更新确保了日期范围的自动管理。 -
DatePicker 是滚轮式选择器:与日历面板式选择器不同,DatePicker 使用滚轮选择年/月/日三列。这种交互适合精确日期选择,但不适合需要"看整个月"的场景。
DatePicker 是日期选择场景中的声明式方案——不需要日历算法、不需要手动范围校验、不需要农历库。三行声明(start + end + selected)替代了过去需要几十行代码的日历选择功能。
七、扩展思考
DatePicker 解决了标准日期选择需求,但在实际项目中,日期选择还有更多变化:
日期面板 vs 日期滚轮:DatePicker 是滚轮式选择器,适合精确选择具体日期。日历面板式选择(如酒店预订中的入住/退房日历,带有价格标记、节假日标记)需要自定义开发——DatePicker 不支持自定义日期单元格样式。
时分秒选择:DatePicker 只选择日期(年/月/日),不包括时间(时/分/秒)。如果需要同时选择日期和时间,需要结合 TimePicker 组件或使用 DatePickerDialog 的 showTime 选项。
DatePickerDialog:当不希望 DatePicker 占用页面空间时,可以使用弹窗模式——DatePickerDialog.show()。弹窗模式的选项更多(如确认/取消按钮样式、背景模糊效果),但它是一个模态弹窗,与内联 DatePicker 的交互模型不同。
日期格式化:DatePicker 本身不提供日期格式化功能——onDateChange 返回的是 Date 对象,开发者需要自行格式化为 yyyy-MM-dd、MM月dd日 等字符串格式。Demo 中的 formatDate() 方法就是典型的日期格式化实现。
国际化和本地化:DatePicker 的星期和月份名称跟随系统语言自动切换。lunar() 模式下的农历文本也支持中英文环境。但在某些特定场景(如需要显示多种语言的日期),需要额外的本地化处理。
性能考量:DatePicker 本身是一个相对重的组件(包含年/月/日三列滚轮)。一页上放置两个 DatePicker(如 Demo 中的出发/返回),在低端设备上滚动可能有轻微延迟。对于性能敏感的场景,可以考虑按需渲染(点击展开 DatePicker,选择后折叠)。
理解 DatePicker 的定位——标准日期滚轮选择器——是正确使用它的关键。它是"选择日期"需求的标准解,但不是所有"日期相关 UI"的通用解。日历面板、日期范围选择器、日期标记等需求需要不同的实现方案。
通过本文的 Demo——出行日期选择,你将 DatePicker 的核心 API 和双选择器联动模式应用到实际的旅行预订场景中,构建了一个完整的日期选择交互流程。这个模式可以直接作为任何出行、预订、日程应用的日期选择起点模板。
更多推荐




所有评论(0)