ArkTS省市区三级联动选择器实现详解
在鸿蒙应用开发中,省市区三级联动是一个常见的功能需求,如收货地址选择、用户信息注册等场景。本文将基于ArkTS语言,详细介绍如何实现一个高效、可复用的省市区三级联动选择器
功能特点
•支持省、市、区三级数据联动
•平滑的滚动选择体验
•高性能的数据加载与渲染
•灵活的定制化选项
实现原理
三级联动选择器的核心原理是基于数据驱动视图更新。当用户选择省份时,城市列表会根据所选省份动态更新;同样,选择城市后,区县列表也会相应变化。
关键技术点
•使用TextPicker组件实现滚动选择
•采用@State装饰器管理组件状态
•通过异步数据加载提升用户体验
•利用函数防抖优化性能
基础实现
数据结构定义
首先需要定义省市区数据的结构模型:
// 省份信息模型
class ProvinceBean {id: number;code: string;label: string;
}
// 城市信息模型
class CityBean {id: number;province_code: string;code: string;label: string;
}
// 区县信息模型
class DistrictBean {id: number;city_code: string;code: string;label: string;
}
[4](@ref)
基础组件框架
@Entry
@Component
struct AreaPickerPage {// 控制弹窗显示/隐藏@State isPickerVisible: boolean = false;// 省市区数据数组@State provinceData: ProvinceBean[] = [];@State cityData: CityBean[] = [];@State districtData: DistrictBean[] = [];// 当前选中的索引@State selectedProvinceIndex: number = 0;@State selectedCityIndex: number = 0;@State selectedDistrictIndex: number = 0;
build() {Column() {// 页面内容Button('选择地址').onClick(() => {this.isPickerVisible = true;})}.bindSheet(this.isPickerVisible, this.pickerBuilder(), {height: 300})}
@BuilderpickerBuilder() {// 选择器内容}
}
[1](@ref)
完整实现方案
数据加载与初始化
应用启动时加载初始数据:
async aboutToAppear() {// 获取省份数据const provinces = await this.loadProvinceData();this.provinceData = provinces;if (provinces.length > 0) {// 获取第一个省份的城市数据const cities = await this.loadCityData(provinces[0].code);this.cityData = cities;if (cities.length > 0) {// 获取第一个城市的区县数据const districts = await this.loadDistrictData(cities[0].code);this.districtData = districts;}}
}
[1](@ref)
联动选择器实现
@Builder
pickerBuilder() {Column() {// 顶部操作栏Row() {Text('取消').onClick(() => {this.isPickerVisible = false;})Blank()Text('确定').onClick(() => {this.onAddressSelected();this.isPickerVisible = false;})}.width('100%').padding(20)
// 三级选择器Row() {// 省份选择TextPicker({ range: this.getProvinceNames(), selected: this.selectedProvinceIndex }).onChange((value: string, index: number) => {this.onProvinceChange(index);}).layoutWeight(1)
// 城市选择 TextPicker({ range: this.getCityNames(), selected: this.selectedCityIndex }).onChange((value: string, index: number) => {this.onCityChange(index);}).layoutWeight(1)
// 区县选择TextPicker({ range: this.getDistrictNames(), selected: this.selectedDistrictIndex }).layoutWeight(1)}.height(200)}
}
[7](@ref)
联动逻辑处理
// 省份变更处理
async onProvinceChange(index: number) {// 使用防抖避免频繁触发if (this.debounceTimer) {clearTimeout(this.debounceTimer);}this.debounceTimer = setTimeout(async () => {this.selectedProvinceIndex = index;const province = this.provinceData[index];// 加载城市数据this.cityData = await this.loadCityData(province.code);this.selectedCityIndex = 0;if (this.cityData.length > 0) {// 加载区县数据this.districtData = await this.loadDistrictData(this.cityData[0].code);this.selectedDistrictIndex = 0;}}, 300);
}
// 城市变更处理
async onCityChange(index: number) {this.selectedCityIndex = index;const city = this.cityData[index];// 加载区县数据this.districtData = await this.loadDistrictData(city.code);this.selectedDistrictIndex = 0;
}
[1](@ref)
性能优化
函数防抖处理
在处理快速滚动时,通过函数防抖避免频繁请求:
@State debounceTimer: number = -1;
// 防抖函数
debounce(func: Function, delay: number) {return (...args: any[]) => {if (this.debounceTimer !== -1) {clearTimeout(this.debounceTimer);}this.debounceTimer = setTimeout(() => {func.apply(this, args);}, delay);};
}
[1](@ref)
数据缓存策略
实现数据缓存避免重复请求:
// 数据缓存对象
private static dataCache: Map<string, any> = new Map();
// 带缓存的数据加载
async loadDataWithCache(url: string): Promise<any> {if (AreaPickerPage.dataCache.has(url)) {return AreaPickerPage.dataCache.get(url);}const data = await this.requestData(url);AreaPickerPage.dataCache.set(url, data);return data;
}
[4](@ref)
高级功能扩展
自定义样式配置
支持深色模式适配和自定义主题:
@Extend(TextPicker)
function customPickerStyle() {.selectedTextStyle({color: $r('app.color.picker_selected'),font: { size: 18, weight: FontWeight.Medium }}).textStyle({color: $r('app.color.picker_normal'),font: { size: 16 }})
}
[7](@ref)
云端数据集成
支持从云端动态加载地址数据:
// 调用云函数获取省份数据
async loadProvinceFromCloud() {try {const functionCallable = agconnect.function().wrap("province-query-$latest");const result = await functionCallable.call({});this.provinceData = result.getValue().result;} catch (error) {console.error('Failed to load province data:', error);}
}
[3,4](@ref)
组件封装与复用
将三级联动选择器封装为独立组件,便于复用:
@Component
export struct AreaPickerComponent {@Prop onConfirm: (province: ProvinceBean, city: CityBean, district: DistrictBean) => void;@Prop onCancel: () => void;@Link selectedProvince: ProvinceBean;@Link selectedCity: CityBean; @Link selectedDistrict: DistrictBean;
build() {// 组件实现}
}
调用方式:
// 在父组件中使用
AreaPickerComponent({onConfirm: (province, city, district) => {console.log(`选中: ${province.label} ${city.label} ${district.label}`);},onCancel: () => {console.log('选择取消');}
})
[2,7](@ref)
总结
通过ArkTS实现省市区三级联动选择器,关键点在于:合理的状态管理、高效的数据加载和流畅的联动交互。本文介绍了从基础实现到高级优化的完整方案,开发者可以根据实际需求进行调整和扩展。
