在鸿蒙应用开发中,省市区三级联动是一个常见的功能需求,如收货地址选择、用户信息注册等场景。本文将基于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
})
}
@Builder
pickerBuilder() {
// 选择器内容
}
}
[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 = new Map();
// 带缓存的数据加载
async loadDataWithCache(url: string): Promise {
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实现省市区三级联动选择器,关键点在于:合理的状态管理、高效的数据加载和流畅的联动交互。本文介绍了从基础实现到高级优化的完整方案,开发者可以根据实际需求进行调整和扩展。
浙公网安备 33010602011771号