鸿蒙地址选择库(ArkTs UI)
功能点:支持三级联动、点击确认返回省市区code及name(安心)、布局可以高度自定义
实现:TextPicker+读取本地json(也可用第三方的json 不过需要自行调整了)
先上图吧、废话下面再说:
凑和看吧、(可能是虚拟机卡、或者动画原因、联动会慢一些)
调用:
方式一 1、//注意调用处是在ets @Component 组件中let areaData = await AreaDataUtil.loadJsonFile(getContext())GlobalAreaPicker.show(this.getUIContext(),{areaData: areaData,onConfirm: (province, city, county,addr) => {//或者直接用addrconsole.log(`选中:${province.name} ${city.name} ${county.name}`);},onCancel: () => {console.log('取消选择');}})方式二 2、//调用注意调用处是在ets @Component 组件中showAreaPicker(this.getUIContext(), (province, city, county,addr) => {// this.companyAddress = `${province.name} ${city.name} ${county.name}`;})
1、主要类说明看截图
补充代码截图
2、上代码 :
2.1 GlobalAreaPicker.ets(核心实现类)
// GlobalAreaPicker.ets
import { JsonAddr } from '../utils/AreaDataUtil';
import { ComponentContent } from '@kit.ArkUI';
import { FontDiy, TextStyleModifier } from './style/TextStyles';
import { IS_PREVIEW } from 'models';
import { DIVIDER_COLOR } from '../constant/ColorConstant';//地址选择器参数传递
export interface AreaSelectOption {areaData: JsonAddr[],onConfirm?: (province: JsonAddr, city: JsonAddr, county: JsonAddr,addressInfo:string) => void,//addressInfo 拼接好的地址 默认空格隔开onCancel?: () => void// 反正我没用着、留着呗
}//全局弹窗封装、不依赖沙雕Pager
export class GlobalAreaPicker {static contentNode?: ComponentContent<Object>static show(uiContext: UIContext, options: AreaSelectOption) {GlobalAreaPicker.contentNode = new ComponentContent(uiContext,wrapBuilder(buildAreaPicker),options);uiContext.getPromptAction().openCustomDialog(GlobalAreaPicker.contentNode, {alignment: DialogAlignment.Bottom,autoCancel: true,});}static close(uiContext: UIContext) {if (GlobalAreaPicker.contentNode !== null) {uiContext.getPromptAction().closeCustomDialog(GlobalAreaPicker.contentNode)}}
}//全局弹窗需要、甲鱼的屁股-》规定
@Builder
function buildAreaPicker(params: AreaSelectOption) {AreaPickerContent({areaData: params.areaData,onConfirm: params.onConfirm,onCancel: params.onCancel})
}// 弹窗内容组件(开业)
// @Preview 需要虚拟机运行才行、
@Component
struct AreaPickerContent {@Prop areaData: JsonAddr[] = [];@State selectedProvince: JsonAddr | null = null;@State selectedCity: JsonAddr | null = null;@State selectedCounty: JsonAddr | null = null;@State provinceIndex: number = 0;@State cityIndex: number = 0;@State countyIndex: number = 0;// TextPicker 数据@State provinceNames: string[] = [];@State cityNames: string[] = [];@State countyNames: string[] = [];onConfirm?: (province: JsonAddr, city: JsonAddr, county: JsonAddr,addressInfo:string) => void;onCancel?: () => void;aboutToAppear() {this.provinceNames = this.areaData.map(p => p.name);this.initDefaultSelection();}initDefaultSelection() {if (this.areaData.length > 0) {this.selectedProvince = this.areaData[0];this.provinceIndex = 0;this.updateCities();}}updateCities() {if (this.selectedProvince?.children && this.selectedProvince.children.length > 0) {this.cityNames = this.selectedProvince.children.map(c => c.name);this.selectedCity = this.selectedProvince.children[0];this.cityIndex = 0;this.updateCounties();}}updateCounties() {if (this.selectedCity?.children && this.selectedCity.children.length > 0) {this.countyNames = this.selectedCity.children.map(c => c.name);this.selectedCounty = this.selectedCity.children[0];this.countyIndex = 0;}}build() {Column() {// 顶部操作栏Row() {Text('取消').attributeModifier(new TextStyleModifier(16, '#999999', FontDiy.MEDIUM, IS_PREVIEW)).onClick(() => {GlobalAreaPicker.close(this.getUIContext());this.onCancel?.();})Blank()Text('确认').attributeModifier(new TextStyleModifier(16, '#1778FF', FontDiy.MEDIUM, IS_PREVIEW)).onClick(() => {if (this.selectedProvince && this.selectedCity && this.selectedCounty) {this.onConfirm?.(this.selectedProvince,this.selectedCity,this.selectedCounty,`${this.selectedProvince.name}${this.selectedCity.name.includes('市辖区')?' ':` ${this.selectedCity.name} `}${this.selectedCounty.name}`//拼接字符串地址(过滤掉市辖区));}GlobalAreaPicker.close(this.getUIContext());})}.width('100%').height(44).padding({ left: 16, right: 16 })Divider().color('#f0f0f0')Row() {// 省份TextPicker({ range: this.provinceNames, selected: this.provinceIndex }).width('33.33%').height(250).canLoop(false).selectTextStyle()//往下扒拉扒拉 能找到.onChange((value: string | string[], index: number | number[]) => {if (typeof index === 'number') {this.provinceIndex = index;this.selectedProvince = this.areaData[index];this.updateCities();}})// 城市TextPicker({ range: this.cityNames, selected: this.cityIndex }).width('33.33%').height(250).canLoop(false).selectTextStyle().onChange((value: string | string[], index: number | number[]) => {if (typeof index === 'number' && this.selectedProvince?.children) {this.cityIndex = index;this.selectedCity = this.selectedProvince.children[index];this.updateCounties();}})// 区县TextPicker({ range: this.countyNames, selected: this.countyIndex }).width('33.33%').height(250).canLoop(false).selectTextStyle().onChange((value: string | string[], index: number | number[]) => {if (typeof index === 'number' && this.selectedCity?.children) {this.countyIndex = index;this.selectedCounty = this.selectedCity.children[index];}})}}.backgroundColor(Color.White).borderRadius({ topLeft: 12, topRight: 12 })}
}
//扩展属性
@Extend(TextPicker)
function selectTextStyle() {.selectedTextStyle({color: '#333333', font: {size: 18, family:$r('[base].media.pf_medium')}})//报错 删了family就行、自定义字体.textStyle({color: '#999999', font: {size: 16, family:$r('[base].media.pf_regular')}}).disappearTextStyle({color: '#999999', font: {size: 14, family:$r('[base].media.pf_regular')}}).divider({color: DIVIDER_COLOR,//#d5d5d5} as DividerOptions)
}/*
// 使用方式
GlobalAreaPicker.show({areaData: yourAreaData,onConfirm: (province, city, county) => {console.log(`选中:${province.name} ${city.name} ${county.name}`);},onCancel: () => {console.log('取消选择');}
});*/
2.2 AreaDataUtil.ts(读取地址json)
import { logger } from "../../../mock/baseMock";
import { Context } from "@kit.AbilityKit";
import { util } from "@kit.ArkTS";// utils/AreaDataUtil.ets
export class AreaDataUtil {private static JsonAddr: JsonAddr[] = []//读取本地json文件static async loadJsonFile(context: Context): Promise<JsonAddr[]> {if (this.JsonAddr.length > 0) {logger.info('loadJsonFile stop read reason: has address '+AreaDataUtil.JsonAddr.length)return AreaDataUtil.JsonAddr}try {logger.info('开始读取地址 ')const mgr = context.resourceManager;const value = await mgr.getRawFileContent('newprovince.json');const textDecoder = util.TextDecoder.create('utf-8');const jsonStr = textDecoder.decodeWithStream(value);logger.info('地址读取完成 ')let ja = JSON.parse(jsonStr) as JsonAddr[]AreaDataUtil.JsonAddr = jareturn ja;} catch (error) {console.error('读取JSON失败', error);return null;}}
}//地址选择
/*export interface AreaList {province_list: Record<string, string>city_list: Record<string, string>county_list: Record<string, string>test: CascaderOption[]
}*///映射第三库的地址
export interface CascaderOption {text: stringvalue: stringchildren?: CascaderOption[]
}//本地地址json newprovince.json
export interface JsonAddr {name: stringcode: stringchildren?: JsonAddr[]
}
2.3 TextStyleModifier.ets 自定义字体 (不重要、可以不用加、只需要删除text中的.attributeModifier(xxxxx)属性即可)
export class TextStyleModifier implements AttributeModifier<TextAttribute> {fontSize?: number | string | ResourcefontColor?: ResourceColorfontFamily?: string | Resource | FontDiy = FontDiy.UNSETisPreview :boolean = falseconstructor(fontSize: number | string | Resource,fontColor?: ResourceColor,fontFamily?: string | Resource| FontDiy,isPreview:boolean = false) {this.fontSize = fontSizethis.fontColor = fontColorthis.fontFamily = fontFamilythis.isPreview = isPreview}applyNormalAttribute(instance: TextAttribute): void {if (this.fontSize !== undefined) {instance.fontSize(this.fontSize)}if (this.fontColor !== undefined) {instance.fontColor(this.fontColor)}if (this.fontFamily !== undefined) {//枚举类型if (this.isFontDiy(this.fontFamily)) {if(this.isPreview) {return}switch (this.fontFamily) {case FontDiy.REGULAR:// 处理 REGULARinstance.fontFamily($r('[base].media.pf_regular'))breakcase FontDiy.MEDIUM:instance.fontFamily($r('[base].media.pf_medium'))// 处理 MEDIUMbreakcase FontDiy.SEMI_BOLD:instance.fontFamily($r('[base].media.pf_semibold'))// ...breakdefault:// 默认处理instance.fontFamily($r('[base].media.youshebiaotihei'))break}} else {//不确定是否有问题instance.fontFamily(`${this.fontFamily}`)}}}// 类型守卫函数isFontDiy(value: string | Resource | FontDiy): boolean {return Object.values(FontDiy).includes(value as FontDiy);}
}export enum FontDiy {UNSET,REGULAR,MEDIUM,SEMI_BOLD,YOU_SHE_BIAO_TIHEI
}
2.4 PickerGlobalFun.ets
//地址选择器
export async function showAreaPicker(uiContext: UIContext, call?: (province: JsonAddr, city: JsonAddr,county: JsonAddr,addressInfo:string) => void) {//去读本地数据数据let areaData = await AreaDataUtil.loadJsonFile(getContext())GlobalAreaPicker.show(uiContext, {areaData: areaData,onConfirm:call,})
}
相关代码基本就这么多、加到自己工程里、需要自己稍微调整下 部分变量如IS_PREVIEW找不到可以使用false代替
别大意=》(注意文件类型、注意文件类型、注意文件类型 如ets/ts)
3、json地址文件
文件地址:https://download.csdn.net/download/BirdEatBug/91801354
至此基本over
扩展:
1、环境
硬件环境:DevEco Studio 5.0.0
鸿蒙sdk版本(Compatible Sdk):12
2、json数据源是否可以自定义?
可以
放心目前用的也就是最近两三年的地址json、当然有的需求不一样需要用自己的服务器的地址json
只需要调整JsonAddr 就行了(同时修改AreaPickerContent 所涉及的JsonAddr 字段即可)
修改JsonAddr参考AreaDataUtils 中
let ja = JSON.parse(jsonStr) as JsonAddr[]//改成自己定义的接口就行。 //原来数据源定义如下、可按照需求调整 export interface JsonAddr {name: string// 省市区 名称code: string//对应如上的地址编码children?: JsonAddr[]//市区/市县 }
3、是否有推荐的第三方地址json库
这个:OpenHarmony三方库中心仓
github:https://github.com/ibestservices/area-data 这两个是同一个库、地址维护比较新