鸿蒙新特性:TextPicker 级联选择器详解——构建智能地址选择器
在移动应用中,地址选择是一项高频且有一定复杂度的交互。从省到市再到区,三层级联关系需要选择器具备"上级联动下级"的能力。HarmonyOS NEXT ArkUI 的 TextPicker 组件就是为这类场景而生的——它不仅支持单列文本选择,更支持多列模式和级联数据,天然适配地址选择、分类筛选等层级化选择场景。
本文将从 TextPicker 的基础 API 出发,深入讲解级联模式的原理与实现,并构建一个完整的"地址选择器"——包含省市区的三层级联、快捷城市预设和层级切换功能。
关键词:HarmonyOS、ArkUI、TextPicker、级联选择、地址选择器、联动
一、TextPicker 组件概览
TextPicker 是 ArkUI 的通用文本选择器组件,采用滚轮交互模式,支持三种数据结构:
| 模式 | range 类型 | 列数 | 典型场景 |
|---|---|---|---|
| 单列 | string[] |
1 | 性别、血型、星座 |
| 多列 | string[][] |
2+ | 身高(米+厘米)、车牌(省+编号) |
| 级联 | TextCascadePickerRangeContent[] |
2+ | 地址、行业分类、组织架构 |
其中级联模式是 TextPicker 最强大的特性。它通过 TextCascadePickerRangeContent 定义层级化的树形数据,当用户在上一级做出选择后,下一级的选项会自动更新为对应子节点。这种联动行为由组件内部自动处理,开发者只需提供正确的数据结构。
二、核心 API
2.1 构造函数
TextPicker({
range: string[] | string[][] | TextCascadePickerRangeContent[],
selected: number | number[],
value: string | string[]
})
参数说明:
range:数据源。可以是简单的字符串数组、二维数组或级联结构selected:初始选中项的索引数组,每个元素对应该列的选中位置value:初始选中项的文本数组,与selected对应
2.2 TextCascadePickerRangeContent 接口
这是级联模式的核心数据类型:
interface TextCascadePickerRangeContent {
text: string | Resource; // 当前节点的显示文本
children?: TextCascadePickerRangeContent[]; // 子节点列表
}
这个接口非常简单——每个节点有 text(显示文本)和可选的 children(子节点)。顶层数组是第一级选项(如省份),每个选项的 children 是第二级选项(如城市),以此类推。
2.3 onChange 回调
当用户滑动滚轮改变选择时触发:
.onChange((value: string | string[], index: number | number[]) => {
// value: 当前选中的文本值(级联模式下为 string[])
// index: 当前选中的索引位置(级联模式下为 number[])
})
在级联模式下,value 和 index 都是数组类型,长度等于当前列数。
2.4 关键属性
| 属性 | 类型 | 说明 |
|---|---|---|
.defaultPickerItemHeight |
number | string |
每列选项的高度 |
.canLoop |
boolean |
是否循环滚动,默认 true |
.disappearTextStyle |
PickerTextStyle |
非选中项的文字样式 |
.selectedTextStyle |
PickerTextStyle |
选中项的文字样式 |

三、实战:地址选择器
我们来实现一个完整的地址选择器——支持省/市/区三级级联、6 个快捷城市预设和层级切换(在 2 级和 3 级之间切换)。
3.1 数据准备
首先定义级联数据结构。我们使用 5 个省级区域,每个省下有 3-4 个城市,每个城市下有 3-4 个区/街道:
private fullData: RegionNode[] = [
{
text: '北京市',
children: [
{
text: '东城区',
children: [
{ text: '东华门街道' }, { text: '景山街道' }, { text: '安定门街道' }
]
},
{
text: '西城区',
children: [
{ text: '西长安街街道' }, { text: '金融街街道' }, { text: '什刹海街道' }
]
},
{
text: '朝阳区',
children: [
{ text: '望京街道' }, { text: '三里屯街道' }, { text: '国贸' }
]
},
{
text: '海淀区',
children: [
{ text: '中关村街道' }, { text: '五道口街道' }, { text: '上地街道' }
]
}
]
},
// ... 上海市、广东省、浙江省、四川省
];
这是标准的 TextCascadePickerRangeContent[] 格式——顶层是省份,children 是城市,城市的 children 是区/街道。
3.2 动态构建 2 级数据
为了支持层级切换,我们在 aboutToAppear 中动态构建一份不含区/街道的 2 级数据:
aboutToAppear(): void {
this.twoLevelData = [];
for (let i = 0; i < this.fullData.length; i++) {
const prov = this.fullData[i];
const cityList: RegionNode[] = [];
if (prov.children) {
for (let j = 0; j < prov.children.length; j++) {
cityList.push({ text: prov.children[j].text }); // 不包含 children
}
}
this.twoLevelData.push({ text: prov.text, children: cityList });
}
this.updateAddress();
}
通过遍历原始数据并剥离城市节点下的 children,我们得到一份干净的 2 级级联结构。Toggle 开关切换时,直接切换 range 的数据源即可。
3.3 TextPicker 集成
将数据接入 TextPicker,根据 showDistrict 状态动态选择 2 级或 3 级数据:
TextPicker({
range: this.showDistrict ? this.fullData : this.twoLevelData,
selected: this.showDistrict
? this.selectedIdx
: [this.selectedIdx[0], this.selectedIdx[1]],
value: this.selectedVal
})
.onChange((value: string | string[], index: number | number[]) => {
this.onPickerChange(value, index);
})
关键设计点:2 级模式的 selected 数组长度是 2(省、市),3 级模式长度是 3(省、市、区)。切换模式时需要同步调整 selected 和 value 的数组长度。
3.4 选择结果展示
地址选择的结果展示在一个信息卡片中——显示当前选中的完整地址和对应的索引路径:
Column() {
Row() {
Text('📍').fontSize(22).margin({ right: 10 })
Text(this.addressText)
.fontSize(18).fontColor('#1a1a2e').fontWeight(FontWeight.Bold)
}
Row() {
Text('层级:').fontSize(11).fontColor('#9999AA')
Text(this.selectedIdx.join(' → '))
.fontSize(11).fontColor('#9999AA')
}
.margin({ top: 8 })
}
.width('100%')
.padding({ left: Spacing.LG, right: Spacing.LG, top: 14, bottom: 14 })
.backgroundColor('#F0F6FF')
卡片使用浅蓝底色(#F0F6FF)与页面其他区域区分,地址文本使用大号加粗字体凸显选择结果,索引路径作为辅助信息以灰色小字展示。
3.5 快捷预设
提供 6 个常用城市/区组合的快捷入口,让用户一键跳转到常见地址:
Row() {
this.PresetBtn('北京 · 朝阳', () => { this.applyPreset(0, 2, 0); })
this.PresetBtn('北京 · 海淀', () => { this.applyPreset(0, 3, 0); })
this.PresetBtn('上海 · 浦东', () => { this.applyPreset(1, 0, 0); })
}
点击任意预设按钮,调用 applyPreset 方法同时更新 selectedIdx 和 selectedVal:
applyPreset(pIdx: number, cIdx: number, dIdx: number): void {
this.selectedIdx = [pIdx, cIdx, dIdx];
const prov = this.fullData[pIdx].text;
const city = this.fullData[pIdx].children![cIdx].text;
const dist = this.fullData[pIdx].children![cIdx].children![dIdx].text;
this.selectedVal = [prov, city, dist];
this.updateAddress();
}
注意预设按钮使用 @Builder 方法提取为可复用组件:
@Builder
PresetBtn(label: string, onClick: () => void) {
Text(label)
.fontSize(12)
.fontColor('#1677FF')
.fontWeight(FontWeight.Medium)
.padding({ top: 8, bottom: 8, left: 12, right: 12 })
.borderRadius(8)
.backgroundColor('#EEF3FF')
.margin({ right: 8 })
.onClick(onClick)
}
3.6 层级切换
使用 Toggle 开关在 2 级和 3 级之间动态切换:
Toggle({ type: ToggleType.Switch, isOn: this.showDistrict })
.onChange((on: boolean) => {
this.showDistrict = on;
this.selectedIdx = [0, 0, 0];
if (on) {
this.selectedVal = ['北京市', '东城区', '东华门街道'];
} else {
this.selectedVal = ['北京市', '东城区'];
}
this.updateAddress();
})
切换时重置选中位置到第一项,并根据新模式设置对应长度的 value 数组。
四、级联原理与自定义联动
TextPicker 的级联行为是自动的——当用户在第一列选择了一个新省份,组件内部会查找该省份的 children 并更新第二列选项。但这仅限于组件内部。
如果需要额外的自定义联动逻辑(如在选择结果卡中实时更新、触发搜索等),你需要在 onChange 回调中处理:
onPickerChange(value: string | string[], index: number | number[]): void {
if (Array.isArray(value)) {
this.selectedVal = value as string[];
}
if (Array.isArray(index)) {
this.selectedIdx = index as number[];
}
this.updateAddress(); // 自定义逻辑:更新结果展示
}
这里的类型判断 Array.isArray(value) 是因为 onChange 的参数类型是联合类型 string | string[]。在级联模式下,参数始终是数组类型(即使只有 2 级时也是 2 元数组)。
五、完整交互流程
- 初始状态:显示 3 级级联选择器,默认选中"北京市 - 东城区 - 东华门街道",结果卡片展示完整地址
- 手动选择:滑动第一列切换省份(例如从"北京市"到"广东省"),第二列自动变为广东省下的城市列表(广州市、深圳市、东莞市、佛山市),第三列相应更新
- 切换层级:关闭"显示街道/镇"开关,选择器变为 2 级(省 - 市),第三列消失
- 快捷预设:点击"深圳 · 南山"按钮,选择器自动跳转到"广东省 - 深圳市 - 南山区"
- 打开层级:重新打开开关,回到 3 级模式,继续精确到街道/镇级别
六、注意事项
6.1 数据完整性
级联数据必须是完整的树形结构——每个节点的 children 要么全部存在,要么全部不存在。不完整的结构(如部分城市有区、部分没有)可能导致级联行为异常。
6.2 索引范围
在 2 级和 3 级之间切换时,selected 索引数组的长度必须与级数匹配。3 级时需要 3 个索引,2 级时需要 2 个。长度不匹配会导致运行时错误。
6.3 数据规模
TextPicker 会在内存中持有完整的级联数据。对于地址选择这类数据量适中的场景(数百个省市、数千个区),性能没有问题。但如果数据量极大(如数十万条),建议考虑异步加载方案。
6.4 与 DatePicker 的区别
TextPicker 和 DatePicker 虽然外观相似(都是滚轮选择),但用途完全不同:
- DatePicker 专用于日期选择,列固定为年/月/日,数据由系统生成
- TextPicker 是通用选择器,列数和数据完全由开发者定义
七、总结
TextPicker 是 ArkUI 中最灵活的选择器组件。从简单的单列选择(性别、血型)到复杂的多级级联(地址、行业),它都能胜任。其核心优势在于:
- 三种模式覆盖所有场景:单列简单、多列独立、级联联动
- 级联行为自动处理:开发者只需提供树形数据,无需手动实现联动逻辑
- 灵活的显示控制:通过 CSS 属性控制样式,适应不同设计需求
本文通过"地址选择器"这个完整的实战案例,覆盖了 TextPicker 级联模式的核心用法——从数据构建到选择器集成、从结果展示到快捷预设、从层级切换到联动逻辑。掌握了这些技术,你就能在任何需要层级化选择的场景中游刃有余。
更多推荐



所有评论(0)