写点什么

智能填充隐藏功能——自动补全地址表单所在地区

作者:HarmonyOS SDK
  • 2025-12-23
    贵州
  • 本文字数:5647 字

    阅读完需:约 19 分钟

在应用程序使用过程中,用户经常需要填写各种表单,例如在寄送包裹时填写收货人信息、购买票务时填写购票人信息、参与调查时填写参与者信息等。这些重复且繁琐的信息填写过程,会直接影响用户的使用体验。为解决这一问题,HarmonyOS SDK融合场景服务(Scenario Fusion Kit)提供了智能填充功能,该功能可根据页面输入框类型、用户已输入内容,为用户提供输入建议,实现复杂表单一键填充。


然而,在填写表单时可能会遇到一个特殊的挑战:当表单中包含所在地区地址选择器时,智能填充不支持对地址选择器进行填充,为了实现地址信息的自动补全,开发者需要对表单中的地址字段进行开发。开发完成后,即使数据源中的"地址所在地区"信息不完整,智能填充服务也能够根据数据源中的详细地址内容,自动推断并补全地址选择器中的所在地区信息。



当"所在地区信息"自动补全后,如果补全内容不符合预期,用户也可以通过点击"地址选择器"重新选择修改。



下面,本文将详细讲解,如何对表单中的地址字段进行开发,实现自动补全地址表单所在地区。

开发准备

  1. 首先,我们需要在 module.json5 文件中设置模糊位置权限:ohos.permission.APPROXIMATELY_LOCATION,允许应用获取设备模糊位置信息。

  2. 其次,所在地区地址选择器需要开通地图服务。

  3. 最后,还需要配置应用签名证书指纹,可参见配置 Client ID。

开发步骤

我们以北京天安门的经纬度为例进行讲解,在获得相关授权后调用获取位置信息的 API,然后根据数据源中现有地址信息遍历当前地址的行政区划层级,自动补全地址表单所在地区,在填写完毕后将表单信息保存到历史表单输入。


import { util } from '@kit.ArkTS';import { i18n } from '@kit.LocalizationKit';import { sceneMap, site } from '@kit.MapKit';import { hilog } from '@kit.PerformanceAnalysisKit';import { BusinessError } from '@kit.BasicServicesKit';import { geoLocationManager } from '@kit.LocationKit';import { abilityAccessCtrl, autoFillManager, common, PermissionRequestResult, Permissions } from '@kit.AbilityKit';
const AUTHED = 0;const TIME_OUT = 100;// Default longitude and latitude. The following uses the longitude and latitude of Tiananmen, Beijing as an example.const INIT_LAT = 39.5;const INIT_LON = 116.2;const ENGLISH = 'en';const SIMPLIFIED_CHINESE = 'zh_CN';const PERMISSIONS: Array<Permissions> = ['ohos.permission.APPROXIMATELY_LOCATION'];const ADMINISTRATIVE_REGION: Array<string> = ['countryName', 'adminLevel1', 'adminLevel2', 'adminLevel3', 'adminLevel4'];
interface PersonInfo { name?: string; phone?: string; email?: string; idCard?: string; region?: string; stressAddress?: string;}
interface RequestParam { requestTag: string; requestText: string;}
interface Location { latitude: number; longitude: number;}
// Display the authorization pop-up.async function reqPermissionsFromUser(permissions: Array<Permissions>, context: common.UIAbilityContext): Promise<PermissionRequestResult> { let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager(); return await atManager.requestPermissionsFromUser(context, permissions);}
// Throttle function.function debounce(func: () => void, wait: number = TIME_OUT): Function { let timeout: number | null = null; return () => { timeout && clearTimeout(timeout); timeout = setTimeout(() => { func(); clearTimeout(timeout); }, wait); };}
@Extend(Text)function textStyle() { .width(64) .textAlign(TextAlign.End)}
@Entry@Componentstruct Index { @State personInfo: PersonInfo = {}; @State isClicked: boolean = false; // Whether the user has triggered information input. private isUserInput: boolean = false; private location: Location = { latitude: INIT_LAT, longitude: INIT_LON, }; private currentRequestTag: string = ''; private handleAddressChange = (request: RequestParam) => { return debounce(async () => { this.autoCompleteAddress(request); }); };
aboutToAppear() { reqPermissionsFromUser(PERMISSIONS, getContext(this) as common.UIAbilityContext) .then((permissionRequestResult: PermissionRequestResult) => { if (permissionRequestResult.authResults[0] === AUTHED) { // The API for obtaining location information can be called only under authorization. geoLocationManager.getCurrentLocation((err, location: geoLocationManager.Location) => { if (err) { hilog.error(0x0000, 'testTag', `Failed to get location, code: ${err?.code}, message: ${err?.message}`); return; } hilog.info(0x0000, 'testTag', `Succeeded in obtaining the current location of the user`); this.location.latitude = location.latitude; this.location.longitude = location.longitude; }) } }) .catch((err: BusinessError) => { hilog.error(0x0000, 'testTag', `Failed request permissions, code: ${err?.code}, message: ${err?.message}`); }) }
public isUsLanguage(): boolean { let result: string = ''; try { result = i18n.System.getSystemLanguage(); } catch (error) { hilog.error(0x0000, 'testTag', 'Failed to get system language'); } return result.toLowerCase() === 'en-latn-us'; }
async autoCompleteAddress(request: RequestParam): Promise<void> { try { let params: site.SearchByTextParams = { query: request.requestText, // Longitude and latitude to which search results need to be biased. location: { latitude: this.location.latitude, longitude: this.location.longitude }, language: this.isUsLanguage() ? ENGLISH : SIMPLIFIED_CHINESE, isChildren: true }; const result = await site.searchByText(params); if (result.sites) { let region: string = ''; let addressComponent = result.sites[0].addressComponent; // Traverse the administrative region level of the current address. for (let item of ADMINISTRATIVE_REGION) { if (addressComponent[item] === undefined) { break; } region += addressComponent[item]; } // Prevent repeated searches that may lead to inconsistent results. if (request.requestTag === this.currentRequestTag) { this.personInfo.region = region; } } } catch (error) { hilog.error(0x0000, 'testTag', `Failed to search location, code: ${error.code}, message: ${error.message}`); } hilog.info(0x0000, 'testTag', 'Succeeded in searching location'); }
onRegionClick(): void { // After a user selects an administrative region, display only search results from the selected region to prevent prolonged queries. this.currentRequestTag = util.generateRandomUUID(); let districtSelectOptions: sceneMap.DistrictSelectOptions = { countryCode: 'CN', }; sceneMap.selectDistrict(getContext(this), districtSelectOptions).then((data) => { hilog.info(0x0000, 'testTag', 'SelectDistrict', 'Succeeded in selecting district.'); let region = ''; for (let i = 0; i < data?.districts?.length; i++) { region += data.districts[i].name; } this.personInfo.region = region; }).catch((err: BusinessError) => { hilog.error(0x0000, 'testTag', `Failed to select district, code: ${err.code}, message: ${err.message}`); }); }
searchRegionByAddress(val: string): void { let tag: string = util.generateRandomUUID(); this.currentRequestTag = tag; let param: RequestParam = { requestTag: tag, requestText: val } // For the manual user input scenario, dithering processing is required. For the automatic input scenario of SmartFill, only the query processing is required. if (this.isUserInput) { this.handleAddressChange(param)(); } else { this.autoCompleteAddress(param); } }
build() { Column({ space: 8 }) { Row({ space: 8 }) { Text('姓名').textStyle() TextInput({ text: this.personInfo.name, placeholder: '姓名' }) .layoutWeight(1) .contentType(ContentType.PERSON_FULL_NAME) .onChange((val: string) => { this.personInfo.name = val; }) }
Row({ space: 8 }) { Text('联系电话').textStyle() TextInput({ text: this.personInfo.phone, placeholder: '手机号码' }) .layoutWeight(1) .contentType(ContentType.PHONE_NUMBER) .onChange((val: string) => { this.personInfo.phone = val; }) }
Row({ space: 8 }) { Text('身份证号').textStyle() TextInput({ text: this.personInfo.idCard, placeholder: '身份证信息' }) .layoutWeight(1) .contentType(ContentType.ID_CARD_NUMBER) .onChange((val: string) => { this.personInfo.idCard = val; }) }
Row({ space: 8 }) { Text('邮件地址').textStyle() TextInput({ text: this.personInfo.email, placeholder: '电子邮件信息' }) .layoutWeight(1) .contentType(ContentType.EMAIL_ADDRESS) .onChange((val: string) => { this.personInfo.email = val; }) }
Row({ space: 8 }) { Text('所在地区').textStyle() TextArea({ text: this.personInfo.region, placeholder: '地区信息' }) .layoutWeight(1) .backgroundColor($r('sys.color.ohos_id_color_card_bg')) .placeholderColor($r('sys.color.ohos_id_color_text_secondary')) .fontSize($r('sys.float.ohos_id_text_size_body1')) .fontColor($r('sys.color.ohos_id_color_text_primary')) .onClick(() => this.onRegionClick()) .focusable(false) }
Row({ space: 8 }) { Text('详细地址').textStyle() TextInput({ text: this.personInfo.stressAddress, placeholder: '小区门牌信息' }) .layoutWeight(1) .contentType(ContentType.DETAIL_INFO_WITHOUT_STREET) .onDidInsert(() => { // Triggered when a user inputs data through an input method. this.isUserInput = true; }) .onDidDelete((val: DeleteValue) => { // Triggered when a user deletes data through an input method. if (val?.deleteValue?.length > 0) { this.isUserInput = true; } }) .onChange((val: string) => { this.personInfo.stressAddress = val; if (val && val.trim().length > 0) { this.searchRegionByAddress(val); } else { this.currentRequestTag = util.generateRandomUUID(); this.personInfo.region = ''; } this.isUserInput = false; }) }
Button('保存') .width('50%') .onClick(() => { if (!this.isClicked) { this.isClicked = true; autoFillManager.requestAutoSave(this.getUIContext(), { onSuccess: () => { hilog.info(0x0000, 'testTag', 'Succeeded in saving request'); }, onFailure: () => { hilog.info(0x0000, 'testTag', 'Failed to save request'); } }); setTimeout(() => { this.isClicked = false; }, 2000); } }) } .padding({ left: 16, right: 16 }) .backgroundColor($r('sys.color.ohos_id_color_list_card_bg')) .alignItems(HorizontalAlign.Center) .height('100%') .width('100%') }}
复制代码


了解更多详情>>


访问融合场景服务联盟官网


获取智能填充能力的开发指导文档

用户头像

HarmonyOS SDK

关注

HarmonyOS SDK 2022-06-16 加入

HarmonyOS SDK通过将HarmonyOS系统级能力对外开放,支撑开发者高效打造更纯净、更智能、更精致、更易用的鸿蒙应用,和开发者共同成长。

评论

发布
暂无评论
智能填充隐藏功能——自动补全地址表单所在地区_HarmonyOS NEXT_HarmonyOS SDK_InfoQ写作社区