在移动应用中,地址选择是一项高频且有一定复杂度的交互。从省到市再到区,三层级联关系需要选择器具备"上级联动下级"的能力。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[])
})

在级联模式下,valueindex 都是数组类型,长度等于当前列数。

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(省、市、区)。切换模式时需要同步调整 selectedvalue 的数组长度。

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 方法同时更新 selectedIdxselectedVal

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 元数组)。

五、完整交互流程

  1. 初始状态:显示 3 级级联选择器,默认选中"北京市 - 东城区 - 东华门街道",结果卡片展示完整地址
  2. 手动选择:滑动第一列切换省份(例如从"北京市"到"广东省"),第二列自动变为广东省下的城市列表(广州市、深圳市、东莞市、佛山市),第三列相应更新
  3. 切换层级:关闭"显示街道/镇"开关,选择器变为 2 级(省 - 市),第三列消失
  4. 快捷预设:点击"深圳 · 南山"按钮,选择器自动跳转到"广东省 - 深圳市 - 南山区"
  5. 打开层级:重新打开开关,回到 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 中最灵活的选择器组件。从简单的单列选择(性别、血型)到复杂的多级级联(地址、行业),它都能胜任。其核心优势在于:

  1. 三种模式覆盖所有场景:单列简单、多列独立、级联联动
  2. 级联行为自动处理:开发者只需提供树形数据,无需手动实现联动逻辑
  3. 灵活的显示控制:通过 CSS 属性控制样式,适应不同设计需求

本文通过"地址选择器"这个完整的实战案例,覆盖了 TextPicker 级联模式的核心用法——从数据构建到选择器集成、从结果展示到快捷预设、从层级切换到联动逻辑。掌握了这些技术,你就能在任何需要层级化选择的场景中游刃有余。


Logo

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

更多推荐