在这里插入图片描述

引言

选择器(Picker)是用户输入类应用中最常见的交互组件。比起手动在文本框中输入文字或数字,选择器通过预设选项列表、滚轮、日历等可视化的方式,让用户以更低的认知成本完成数据录入。HarmonyOS NEXT 提供了 Select 下拉选择器、DatePicker 日期选择器、TimePicker 时间选择器三种原生组件,分别对应离散列表选择、日期选择、时间选择三大场景。

示例 92 以「选择器演示」为主题,将三种选择器组合在一个页面中,构建了一个类似「预约信息填写」的表单场景:用户先从下拉列表中选择城市,再通过日期滚轮选择日期,最后通过时间滚轮选择时间,页面底部实时汇总三种选择结果。此外还提供了「重置」按钮,一键恢复所有选择器到默认状态。整个流程涉及 Selectselectedvalue 绑定、DatePickeronChange 回调与日期格式化、TimePicker 的 24 小时制与时间格式化、以及多选择器的状态联动,几乎涵盖了选择器组件的全部核心用法。

这篇文章会严格按源码顺序,先介绍应用的整体功能与布局结构,再拆解三种选择器的核心属性与事件,接着逐段解读 .ets 源码中的选择器配置、格式化方法、重置逻辑,然后分析日期/时间格式化的技巧、SelectOption 数据结构、24 小时制切换,最后给出运行操作指南、可扩展方向与常见问题调试技巧。读完后,你不仅能看懂这一个选择器演示页面,还能举一反三,把它应用到预约表单、订单筛选、日程安排等任何需要选择器组件的场景。

1. 应用概述与功能

「选择器演示」是一个面向表单输入场景的工具型页面,交互路径清晰:选择城市 → 选择日期 → 选择时间 → 查看汇总结果 → 可重置

页面自上而下分为五块区域:顶部返回栏(返回按钮 + 标题「选择器演示」);城市选择卡片(标签 + 当前值 + Select 下拉组件);日期选择卡片(标签 + DatePicker 滚轮);时间选择卡片(标签 + TimePicker 滚轮);汇总结果卡片(城市/日期/时间三行蓝色文字);底部「重置」按钮。

1.1 核心功能清单

  • 城市下拉选择:使用 Select 组件提供北京、上海、广州、深圳四个城市选项,支持显示当前选中值。
  • 日期选择:使用 DatePicker 组件提供日期滚轮,支持年月日三列滚动选择,初始值为当前日期。
  • 时间选择:使用 TimePicker 组件提供时间滚轮,支持时/分两列滚动选择,24 小时制。
  • 实时汇总:页面底部卡片实时显示三种选择器的当前值,用户每次操作都会立即更新。
  • 一键重置:点击「重置」按钮恢复所有选择器到默认值(城市=北京,日期=今天,时间=当前),并弹出提示。

1.2 技术要点一览

整个示例用到的关键技术对「表单选择器」类页面很有代表性:Select 组件的 selected/value/onSelect 三件套、DatePickerselected 初始值与 onChange 回调、TimePickeruseMilitaryTime 24 小时制开关、SelectOption 数据结构的定义、日期时间的格式化方法(pad 补零 + getFullYear/getMonth/getDate/getHours/getMinutes)、以及多个 @State 状态变量之间的联动更新。把这些要点串起来,就构成了一条完整的「选择器交互 → 状态更新 → 格式化 → 汇总展示」的数据流。

2. 核心知识点

在逐段读代码之前,先把选择器演示页面承载的 ArkTS 核心知识讲清楚。

2.1 Select 组件

Select 是 ArkUI 提供的下拉选择器组件,用于从预定义的选项列表中选择一个值:

Select(this.cities)
  .selected(this.cityIdx)
  .value(this.cityText)
  .font({ size: 16, weight: FontWeight.Medium })
  .width('100%')
  .onSelect((index: number, value: string) => {
    this.cityIdx = index;
    this.cityText = value;
  })

Select 组件的核心属性和事件:

  • 数据源:构造函数接收一个 SelectOption[] 数组,每个元素是一个 { value: string } 对象。
  • selected:设置当前选中项的索引,用于高亮显示选中项。
  • value:设置选择器显示的文本,即选中后输入框中展示的文字。
  • onSelect:选择回调,参数为 (index: number, value: string),分别在用户选择某项时触发。

selectedvalue 通常需要同时设置——selected 控制下拉列表中的高亮项,value 控制输入框中显示的文字。如果只设置 value 不设置 selected,下拉列表中不会高亮任何项。

2.2 SelectOption 数据结构

Select 组件的数据源是 SelectOption[] 类型:

private cities: SelectOption[] = [
  { value: '北京' },
  { value: '上海' },
  { value: '广州' },
  { value: '深圳' }
];

SelectOption 是 ArkUI 内置的接口,每个选项是一个包含 value 字段的对象。value 是显示在下拉列表和输入框中的文字。虽然 SelectOption 还支持 icon 字段(选项图标),但本示例只使用了 value 字段,保持简洁。

2.3 DatePicker 组件

DatePicker 是 ArkUI 提供的日期选择器组件,以滚轮的形式展示年、月、日三列:

DatePicker({ selected: this.date })
  .lunar(false)
  .onChange((value: DatePickerResult) => {
    this.date = new Date(value.year!, value.month! - 1, value.day!);
    this.dateText = value.year! + '-' + this.pad(value.month!) + '-' + this.pad(value.day!);
  })

DatePicker 的核心属性和事件:

  • selected:构造参数,设置初始选中的日期,类型为 Date
  • lunar:是否显示农历,false 为公历,true 为农历。
  • onChange:选择回调,参数为 DatePickerResult 类型,包含 yearmonthday 三个字段。

注意 DatePickerResult 中的 month 是 1-based(1~12),而 JavaScript Datemonth 是 0-based(0~11),所以在构造 Date 对象时需要 value.month! - 1

2.4 TimePicker 组件

TimePicker 是 ArkUI 提供的时间选择器组件,以滚轮的形式展示时、分两列:

TimePicker({ selected: this.time })
  .useMilitaryTime(true)
  .onChange((value: TimePickerResult) => {
    this.timeText = this.pad(value.hour) + ':' + this.pad(value.minute);
  })

TimePicker 的核心属性和事件:

  • selected:构造参数,设置初始选中的时间,类型为 Date
  • useMilitaryTime:是否使用 24 小时制,true 为 24 小时制(0~23),false 为 12 小时制(AM/PM)。
  • onChange:选择回调,参数为 TimePickerResult 类型,包含 hourminute 两个字段。

2.5 日期时间格式化

示例定义了两个辅助方法用于格式化日期和时间:

private pad(n: number): string {
  return n < 10 ? '0' + n : n.toString();
}

private formatDate(d: Date): string {
  return d.getFullYear() + '-' + this.pad(d.getMonth() + 1) + '-' + this.pad(d.getDate());
}

private formatTime(t: Date): string {
  return this.pad(t.getHours()) + ':' + this.pad(t.getMinutes());
}

pad 方法是一个通用的补零函数,将个位数前面补零,保证两位显示。例如 pad(3) 返回 "03"pad(15) 返回 "15"

formatDateDate 对象格式化为 yyyy-MM-dd 字符串,注意 getMonth() 返回 0~11,所以需要 +1formatTimeDate 对象格式化为 HH:mm 字符串。这两个方法在 onChange 回调和 resetAll 重置方法中都被调用。

3. 源码逐段解析

现在开始按源码顺序逐段解读 index92.ets,从导入声明到 build 方法,完整展示选择器演示的实现细节。

3.1 导入声明与组件声明

import { router } from '@kit.ArkUI';
import { promptAction } from '@kit.ArkUI';

@Entry
@Component
struct Index92 {

源码开头导入了 router(页面导航)和 promptAction(轻提示),@Entry@Component 标记组件为页面入口。

3.2 状态变量定义

@State cityIdx: number = 0;
@State cityText: string = '北京';
@State date: Date = new Date();
@State time: Date = new Date();
@State dateText: string = '';
@State timeText: string = '';

组件声明了六个 @State 状态变量:

  • cityIdx:城市选择器的选中索引,初始为 0(第一个城市「北京」)。
  • cityText:城市选择器的显示文本,初始为「北京」。
  • date:日期选择器的 Date 对象,初始为当前日期 new Date()
  • time:时间选择器的 Date 对象,初始为当前时间 new Date()
  • dateText:格式化后的日期文本,初始为空(在 aboutToAppear 中设置或由 onChange 更新)。
  • timeText:格式化后的时间文本,初始为空(同上)。

注意 datetime 都初始化为 new Date(),即当前时刻。虽然它们指向同一时刻,但用途不同——date 只关心年月日部分,time 只关心时分部分。

3.3 城市选项数据

private cities: SelectOption[] = [
  { value: '北京' },
  { value: '上海' },
  { value: '广州' },
  { value: '深圳' }
];

城市列表定义了四个一线城市选项,每个选项是一个 { value: string } 对象。使用 SelectOption[] 类型确保与 Select 组件的数据源类型匹配。

3.4 格式化辅助方法

private pad(n: number): string {
  return n < 10 ? '0' + n : n.toString();
}

private formatDate(d: Date): string {
  return d.getFullYear() + '-' + this.pad(d.getMonth() + 1) + '-' + this.pad(d.getDate());
}

private formatTime(t: Date): string {
  return this.pad(t.getHours()) + ':' + this.pad(t.getMinutes());
}

这三个方法已在第 2 节详细介绍过。值得注意的是 formatDategetMonth() + 1 的处理——JavaScript 的 Date.getMonth() 返回 0-based 的月份(0=一月),所以需要加 1 才是人类可读的月份。这是日期处理中最常见的「坑」之一。

3.5 resetAll 重置方法

private resetAll(): void {
  this.cityIdx = 0;
  this.cityText = '北京';
  this.date = new Date();
  this.time = new Date();
  this.dateText = this.formatDate(this.date);
  this.timeText = this.formatTime(this.time);
  promptAction.showToast({ message: '已恢复默认' });
}

resetAll 方法将所有选择器恢复到初始状态:

  1. 城市索引重置为 0,文本重置为「北京」。
  2. 日期和时间重置为当前时刻 new Date()
  3. 日期文本和时间文本通过格式化方法重新计算。
  4. 弹出「已恢复默认」的 Toast 提示。

这里需要特别注意:重置时不仅要修改 @State 变量,还要确保格式化文本也同步更新。如果只重置 datetime 而不更新 dateTexttimeText,汇总卡片中的文本不会刷新。

3.6 build 方法整体结构

build 方法构建了整个页面的 UI 结构,最外层是一个 Column,包含顶部返回栏、城市选择卡片、日期选择卡片、时间选择卡片、汇总结果卡片和重置按钮:

build() {
  Column() {
    // 顶部返回栏
    Row() { ... }
    // 城市选择
    Column() { ... }
    // 日期选择
    Column() { ... }
    // 时间选择
    Column() { ... }
    // 汇总显示
    Column() { ... }
    // 重置按钮
    Button('重置') { ... }
  }
  .width('100%')
  .height('100%')
  .backgroundColor('#f2f3f5')
}

最外层 Column 全屏宽高,浅灰色背景。

3.7 城市选择卡片

Column() {
  Row() {
    Text('选择城市')
      .fontSize(15)
      .fontColor('#333333')
    Blank()
    Text('当前:' + this.cityText)
      .fontSize(14)
      .fontColor('#1a6cff')
  }
  .width('100%')
  .margin({ bottom: 10 })

  Select(this.cities)
    .selected(this.cityIdx)
    .value(this.cityText)
    .font({ size: 16, weight: FontWeight.Medium })
    .width('100%')
    .onSelect((index: number, value: string) => {
      this.cityIdx = index;
      this.cityText = value;
    })
}
.width('90%')
.padding(16)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ top: 16 })

城市选择卡片包含两部分:

  1. 标签行:左侧「选择城市」标签,右侧「当前:北京」蓝色文字,通过 Blank() 弹性分隔。
  2. Select 组件:数据源为 this.cities,绑定 selectedvalue 属性,onSelect 回调更新索引和文本。

卡片整体宽度 90%,白色背景,圆角 12,带内边距,顶部外边距 16,与返回栏保持间距。

3.8 日期选择卡片

Column() {
  Text('选择日期')
    .fontSize(15)
    .fontColor('#333333')
    .width('100%')
    .margin({ bottom: 6 })

  DatePicker({ selected: this.date })
    .lunar(false)
    .onChange((value: DatePickerResult) => {
      this.date = new Date(value.year!, value.month! - 1, value.day!);
      this.dateText = value.year! + '-' + this.pad(value.month!) + '-' + this.pad(value.day!);
    })
}
.width('90%')
.padding(16)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ top: 12 })

日期选择卡片包含标签和 DatePicker 组件。DatePicker 初始选中 this.date(当前日期),关闭农历模式(lunar(false)),onChange 回调中更新 date 对象和 dateText 文本。

注意 onChange 回调中的日期构造:new Date(value.year!, value.month! - 1, value.day!)DatePickerResultmonth 是 1-based,而 Date 构造函数的 month 参数是 0-based,所以需要减 1。非空断言操作符 ! 用于告诉编译器这些值不为 undefinedDatePickerResult 的字段是可选的)。

3.9 时间选择卡片

Column() {
  Text('选择时间')
    .fontSize(15)
    .fontColor('#333333')
    .width('100%')
    .margin({ bottom: 6 })

  TimePicker({ selected: this.time })
    .useMilitaryTime(true)
    .onChange((value: TimePickerResult) => {
      this.timeText = this.pad(value.hour) + ':' + this.pad(value.minute);
    })
}
.width('90%')
.padding(16)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ top: 12 })

时间选择卡片包含标签和 TimePicker 组件。TimePicker 初始选中 this.time(当前时间),启用 24 小时制(useMilitaryTime(true)),onChange 回调中更新 timeText 文本。

注意 TimePickerResulthourminute 字段是必填的(非可选),所以不需要非空断言操作符。

3.10 汇总结果卡片

Column() {
  Text('选择结果')
    .fontSize(15)
    .fontWeight(FontWeight.Bold)
    .fontColor('#333333')
    .margin({ bottom: 10 })
  Text('城市:' + this.cityText)
    .fontSize(16)
    .fontColor('#1a6cff')
    .margin({ bottom: 6 })
  Text('日期:' + this.dateText)
    .fontSize(16)
    .fontColor('#1a6cff')
    .margin({ bottom: 6 })
  Text('时间:' + this.timeText)
    .fontSize(16)
    .fontColor('#1a6cff')
}
.width('90%')
.padding(16)
.backgroundColor('#ffffff')
.borderRadius(12)
.margin({ top: 12 })

汇总结果卡片以蓝色文字展示三种选择器的当前值。由于 cityTextdateTexttimeText 都是 @State 变量,用户每次操作选择器时,这些文本会自动更新,汇总卡片也会同步刷新。

3.11 重置按钮

Button('重置')
  .width(200)
  .height(46)
  .backgroundColor('#1a6cff')
  .fontColor(Color.White)
  .margin({ top: 20 })
  .onClick(() => {
    this.resetAll();
  })

重置按钮宽度 200vp、高度 46vp,蓝色主题,点击后调用 resetAll() 方法恢复所有选择器到默认状态。

4. 交互流程详解

4.1 城市选择流程

用户点击 Select 组件,弹出下拉列表,选择某个城市:

  1. Select 组件展开下拉列表,高亮当前选中项(selected(this.cityIdx) 控制)。
  2. 用户点击某个选项,触发 onSelect 回调,参数为 (index, value)
  3. 回调中更新 this.cityIdx = indexthis.cityText = value
  4. @State 变量变化触发 UI 刷新:Select 的输入框显示新选中的城市名,标签行的「当前」文本更新,汇总卡片的「城市」行更新。

4.2 日期选择流程

用户滑动 DatePicker 的滚轮,选择年/月/日:

  1. 用户滚动滚轮,DatePicker 实时触发 onChange 回调。
  2. 回调中更新 this.date 为新的 Date 对象,并格式化 this.dateText
  3. @State 变量变化触发汇总卡片的「日期」行更新。

4.3 时间选择流程

用户滑动 TimePicker 的滚轮,选择时/分:

  1. 用户滚动滚轮,TimePicker 实时触发 onChange 回调。
  2. 回调中格式化 this.timeText
  3. @State 变量变化触发汇总卡片的「时间」行更新。

4.4 重置流程

用户点击「重置」按钮:

  1. resetAll() 方法被调用。
  2. 所有 @State 变量恢复为初始值。
  3. 城市选择器回到第一个选项,日期和时间选择器回到当前时刻。
  4. 汇总卡片三行文本同步更新。
  5. 弹出「已恢复默认」Toast 提示。

5. UI 样式设计思路

5.1 卡片式布局

每个选择器都包裹在独立的白色圆角卡片中,卡片宽度 90%,圆角 12vp,内边距 16vp,卡片间间距 12vp。这种卡片式布局让页面层次分明,用户可以清晰地识别每个选择器的功能区域。

5.2 主题色统一

整个页面使用统一的主题色 #1a6cff

  • 返回按钮背景色
  • Select 选中文字颜色
  • 汇总卡片中的值文本颜色
  • 重置按钮背景色

统一的主题色让页面视觉风格一致,提升了整体的专业感。

5.3 标签与值分离

城市选择卡片采用了「标签在左,当前值在右」的布局:

选择城市              当前:北京
[  Select 下拉组件    ]

这种布局让用户在操作前就能看到当前值,操作后也能立即对比新旧值,提升了交互的可感知性。

6. 运行与测试

6.1 运行步骤

  1. 使用 DevEco Studio 打开项目。
  2. 运行项目到模拟器或真机。
  3. 在首页找到「选择器演示」示例入口,点击进入。
  4. 点击城市下拉列表,选择不同城市,观察汇总卡片更新。
  5. 滑动日期滚轮,选择不同日期,观察汇总卡片更新。
  6. 滑动时间滚轮,选择不同时间,观察汇总卡片更新。
  7. 点击「重置」按钮,确认所有选择器恢复默认值。

6.2 测试场景

测试场景 预期结果
选择城市「上海」 下拉列表高亮上海,输入框显示上海,汇总更新
滑动日期到 2025-01-15 日期文本更新为 2025-01-15,汇总更新
滑动时间到 14:30 时间文本更新为 14:30,汇总更新
点击「重置」按钮 所有选择器恢复默认,弹出「已恢复默认」提示
快速连续滑动日期滚轮 日期文本实时更新,无卡顿

7. 可扩展方向

7.1 级联选择器

将城市选择扩展为省/市级联选择器——选择省份后,城市列表自动更新为该省份下的城市。需要在 onSelect 回调中动态修改 Select 的数据源。

7.2 农历日期

DatePickerlunar 属性设为 true,即可显示农历日期。可以增加一个开关按钮,让用户在公历和农历之间切换。

7.3 12 小时制切换

TimePickeruseMilitaryTime 设为 false,即可切换到 12 小时制(AM/PM)。可以增加一个 Toggle 开关,让用户自由切换。

7.4 表单提交验证

增加「提交」按钮,在提交前验证所有选择器是否已选择(日期和时间默认有值,城市需要确认不是默认占位符)。验证通过后跳转到确认页面或弹出确认对话框。

7.5 日期范围限制

通过 DatePickerstartend 属性限制可选日期范围,例如只能选择未来 7 天内的日期,适用于预约场景。

8. 常见问题与调试

8.1 Select 选中项不高亮

问题Select 的下拉列表中没有高亮任何项。

排查

  • 确认 selected(this.cityIdx) 是否正确设置。selected 需要传入当前选中项的索引。
  • 检查 cityIdx 的值是否在数据源的有效索引范围内(0~3)。

8.2 DatePicker 月份错误

问题:选择 3 月,但 Date 对象的月份变成了 2 月。

排查

  • 确认 onChange 回调中构造 Date 时是否做了 month - 1 处理。DatePickerResult.month 是 1-based,Date 构造函数的 month 是 0-based。
  • 检查 formatDate 方法中 getMonth() + 1 是否正确——显示时要加回 1。

8.3 重置后文本不更新

问题:点击「重置」按钮后,SelectDatePicker 视觉上恢复了,但汇总卡片的文本没有更新。

排查

  • 确认 resetAll() 方法中是否同时更新了 dateTexttimeText。只修改 datetime 对象不会自动更新格式化文本。
  • resetAll() 中添加日志,确认格式化方法的返回值是否正确。

8.4 TimePicker 显示 12 小时制

问题TimePicker 显示了 AM/PM,而不是预期的 24 小时制。

排查

  • 确认 useMilitaryTime(true) 是否设置。默认可能不是 24 小时制。
  • 检查是否有其他代码覆盖了 useMilitaryTime 的值。

9. 技术总结

示例 92 的选择器演示将三种原生选择器组件有机组合,展示了 ArkUI 选择器体系的完整用法。通过这个示例,我们可以总结出选择器组件的使用范式:

  1. Select 三件套selected 控制高亮、value 控制显示文字、onSelect 处理选择事件,三者缺一不可。
  2. DatePicker 月份转换DatePickerResult.month 是 1-based,Date 的 month 是 0-based,两者之间需要加减 1 转换。
  3. TimePicker 24 小时制useMilitaryTime(true) 开启 24 小时制,适用于需要精确时间的场景。
  4. 格式化辅助方法pad 补零 + formatDate/formatTime 是日期时间格式化的标准模式,复用性高。
  5. 状态联动:每个选择器的 onChange 回调只需更新对应的 @State 变量,ArkUI 的响应式机制会自动处理 UI 刷新。

掌握了这些范式后,就可以轻松地将选择器组件应用到预约表单、日程管理、订单筛选等实际业务场景中。选择器作为表单输入的核心组件,是每个鸿蒙开发者必须熟练掌握的基础技能。

Logo

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

更多推荐