HarmonyOS7 PickerSwitcherPanel 教程:一个页面切换三种选择器

前言
把不同选择器放进一个切换面板后,条件渲染、结果管理和模块分组就都出现了。 真正上手时,难点往往不在组件本身,而在你要不要把它写得像个真实页面。 我不会按属性表的顺序平铺,而是直接挑最影响使用体验的部分来讲。
这篇我会重点从 组件封装角度 这个角度往下讲。不是为了把每个属性都讲一遍,而是帮你建立一个更接近真实开发的阅读顺序:先看页面目标,再看状态流,最后再看样式怎么收口。
这个页面适合放进什么场景
这类示例最怕读成“API 列表”,所以我会直接从页面行为往回拆。
“选择器综合”这种能力,单独演示时很轻,落到项目里却往往要和表单、列表、卡片或者反馈区绑在一起,这也是这个案例值得细看的原因。
这一类写法常见在 筛选面板、报名表单、地址选择、分类联动 这些页面里。你只要把示例里的交互换成自己的业务文案,再把静态数据换成真实数据源,骨架基本就够用了。
示例读不明白时,最好的办法不是多看几遍属性表,而是先把页面里的角色关系搞明白。

如果你后面要把示例改成线上页面,别急着先美化,先确认 状态字段和展示结果有没有保持同一份数据来源,避免页面看起来“能动”但结果不可信。
先看界面层次
页面最外层通常先用 Column 把主轴定下来,这一步看起来普通,但它决定了后面所有内容是顺着读,还是碎着看。
如果代码里出现了 Scroll,那基本说明作者已经预判到内容会超过一屏。这种处理很实在,至少不会等页面做长了再返工。
这一页更多是纵向阅读逻辑,说明作者希望用户按顺序完成操作,而不是在多个区域来回跳。
第 36 篇案例在布局上最值得学的地方,是它没有为了展示组件能力去强行堆内容,而是尽量让每一块区域都有明确职责。这样的代码后面更好拆组件。
完整代码
下面这份代码保留了案例本身的实现思路,但把示例编号替换成了更自然的英文命名。你直接拿去做实验、拆段运行,阅读成本会低很多。

/**
* PickerSwitcherPanel - 选择器综合
*/
import { PRESET_COLORS, generateListItems, PAGE_BG_COLOR, PANEL_BG_COLOR, ACCENT_COLOR, MUTED_TEXT_COLOR } from './types'
@Entry
@Component
struct PickerSwitcherPanel {
@State isShow: boolean = true
@State typeIndex: number = 0
@State dateResult: string = ''
@State timeResult: string = ''
@State textResult: string = ''
private pickerTypes: string[] = ['DatePicker 日期', 'TimePicker 时间', 'TextPicker 文本']
build() {
Column() {
if (this.isShow) {
Scroll() {
Column({ space: 16 }) {
/* 选择器类型切换 */
Column() {
Text('选择器类型').fontSize(14).fontColor(ACCENT_COLOR).margin({ bottom: 8 })
TextPicker({ range: this.pickerTypes, selected: this.typeIndex })
.onChange((value: string | string[], index: number | number[]) => {
this.typeIndex = index as number
})
}
.backgroundColor(PANEL_BG_COLOR).padding(12).borderRadius(12)
/* 根据类型显示对应选择器 */
if (this.typeIndex === 0) {
Column() {
Text('DatePicker').fontSize(14).fontColor('#4D96FF').margin({ bottom: 8 })
DatePicker({ start: new Date(2020, 0, 1), end: new Date(2030, 11, 31), selected: new Date() })
.onChange((value: DatePickerResult) => {
this.dateResult = (value.year ?? 0) + '/' + ((value.month ?? 0) + 1) + '/' + (value.day ?? 0)
})
}.backgroundColor('#E8F4FD').padding(16).borderRadius(12)
} else if (this.typeIndex === 1) {
Column() {
Text('TimePicker').fontSize(14).fontColor('#6BCB77').margin({ bottom: 8 })
TimePicker({ selected: new Date() })
.onChange((value: TimePickerResult) => {
this.timeResult = (value.hour ?? 0) + ':' + (value.minute ?? 0)
})
}.backgroundColor('#F0FAF1').padding(16).borderRadius(12)
} else {
Column() {
Text('TextPicker').fontSize(14).fontColor('#9B59B6').margin({ bottom: 8 })
TextPicker({ range: ['选项A', '选项B', '选项C', '选项D'], selected: 0 })
.onChange((value: string | string[], index: number | number[]) => {
this.textResult = value as string
})
}.backgroundColor('#F5EEF8').padding(16).borderRadius(12)
}
/* 结果汇总 */
Column({ space: 8 }) {
Text('DatePicker结果: ' + this.dateResult).fontSize(13).fontColor('#4D96FF')
Text('TimePicker结果: ' + this.timeResult).fontSize(13).fontColor('#6BCB77')
Text('TextPicker结果: ' + this.textResult).fontSize(13).fontColor('#9B59B6')
}
.width('100%').backgroundColor(PANEL_BG_COLOR).padding(14).borderRadius(12)
}.width('100%')
}
}
Text('选择器综合 - TextPicker+DatePicker+TimePicker')
.fontSize(12).fontColor('#999999').margin({ top: 12 })
}
.width('100%').height('100%').backgroundColor('#F5F6FA').padding(16)
}
}
代码里最该先看的部分
第一处关键代码
@State isShow: boolean = true
这一行先别急着略过,它通常就是页面状态的起点。后面很多显示内容,都会跟着它一起变化。 顺着它往下看,你通常能更快找到页面真正的主逻辑。
第二处关键代码
@State typeIndex: number = 0
看起来只是一个字段定义,但页面后面能不能“动起来”,很多时候就靠它。 顺着它往下看,你通常能更快找到页面真正的主逻辑。
第三处关键代码
@State dateResult: string = ''
这种状态声明的价值,不在写法本身,而在于它决定了谁来保存结果、谁来触发刷新。 顺着它往下看,你通常能更快找到页面真正的主逻辑。
从案例走到业务页面
很多人第一次读示例会从头到尾扫一遍,但更高效的做法,其实是先看下面这些触发点:
.onChange((value: string | string[], index: number | number[]) => {.onChange((value: DatePickerResult) => {.onChange((value: TimePickerResult) => {.onChange((value: string | string[], index: number | number[]) => {
对大多数表单和选择类页面来说,交互的质量基本取决于回调有没有把结果及时、清楚地反馈出来。
读到交互段时,不妨多问一句:这个结果有没有唯一数据源?很多页面后面难维护,就是从这里埋下的。
一个很实用的改法,是尽早把状态分层:谁负责输入,谁负责展示,谁负责兜底。只要这一步做清楚,后面页面再长,build() 也不至于越来越乱。
再往前走一步
真要把这个案例拿去改业务页面,我会按下面这个顺序动手:
- 把演示用的静态数据替换成真实数据源,别等接口接进来以后再改页面结构。
- 把重复出现的卡片、标题栏、结果区提成小组件,后面加状态会轻松很多。
- 提前补上异常态,比如空值、失败态、禁用态、超范围输入,否则示例一进业务就会露怯。
- 如果是综合选择器,我会优先拆模块,把不同选择区的状态边界切干净,别让一个页面状态互相串。
这里最容易被忽略的一点,是 状态字段和展示结果有没有保持同一份数据来源,避免页面看起来“能动”但结果不可信。 这个问题。很多示例在静态数据下看不出毛病,一接真实状态就开始暴露问题,所以这一步最好趁早做。
容易被忽略的小地方
有些细节在第一眼看代码时很容易被跳过去,但它们往往决定了页面是不是耐改。
@State不是装饰器样板,它决定了哪些数据变化后会重新驱动画面。- 私有数组或字段一般在承担“页面配置”的角色,比如选项范围、颜色映射、联动源数据。
- 对于 Picker 类页面,我会特别留意文案反馈是不是跟着用户动作一起变化,因为这直接影响页面有没有“会说话”的感觉。
这些东西单看都不复杂,组合在一起,才是一个案例真正的完成度。
写在最后
这类页面往往不是功能难,而是容易写得太像示例。只要你把状态、反馈和视觉层次这三件事捏紧,页面质感就会立刻上来。
先把数据从哪来、结果往哪去想清楚,再去调样式,返工会少很多。
更多推荐


所有评论(0)