写点什么

HarmonyOS 实战: 城市选择功能的快速实现

作者:IT小码哥
  • 2025-05-27
    北京
  • 本文字数:2312 字

    阅读完需:约 8 分钟

前言

最近在日常开发过程中,需要实现城市选择功能,同时支持模糊搜索。看似简单的功能动手实现起来却有很多难点。本篇文章详细记录开发过程中遇到的问题和对应的解决方法,希望能够帮助你,建议点赞收藏!

实现效果

需求分析

  • 处理城市列表中的多音字。

  • 城市列表要按照字母表的顺序排列

  • 将首字母相同的城市分组。

技术实现

  1. 在日常开发中,城市数据一般由接口获取或从本地 json 文件读取,毕竟城市数量和城市编码是固定不变的。不管是哪种方式获取城市数据,都无法保证数据的顺序符合需求,在处理数据顺序之前,首先要处理数据中的多音字,对常见多音字手动添加拼音字母,其余城市直接获取首字母,这里需要借助一个三方库 pinyin4js 获取汉字的第一个拼音字母。


data?.forEach((city) => {            if (city.cityName?.includes("长沙")) {              city.letter = "CHANGSHASHI"            } else if (city.cityName?.startsWith("重庆")) {              city.letter = "CHONGQING"            } else if (city.cityName?.startsWith("厦门")) {              city.letter = "XIAMENSHI"            } else {              city.letter = pinyin4js.convertToPinyinString(city.cityName, '', pinyin4js.FIRST_LETTER).toUpperCase()            }
})
复制代码


  1. 得到城市的字母后,这时就需要对城市数据按字母排序进行处理,借助系统通过 collator 类的 compare 方法,对数据的字母进行排序。


      let collator = new intl.Collator();        data.sort((firstCity: HotCityBean, secondCity: HotCityBean) => {          return collator.compare(firstCity.letter, secondCity.letter)        })
复制代码


  1. 得到排序后的数据,需要对数据进行分组,将首字母相同的城市放在同一个数组里,这一步有点复杂,需要慢慢看。



let target = data[0].letter?.charAt(0) ?? "" let cityType = new HotCityTypeBean() cityType.name = target; data.forEach((value, number) => { if (value.letter?.charAt(0) != target) { this.cityList.push(cityType) target = value.letter?.charAt(0) ?? "" cityType = new HotCityTypeBean() cityType.name = target cityType.city.push(value) } else { cityType.city.push(value) } ....... })
复制代码


首先得到第一条城市的首字母 A 作为目标项,然后使用 forEach 遍历数据,如果遍历的对象的首字母和目标项 A 不相等,则先认为首字母 A 的城市遍历结束,直接将相同字母 A 的数据放入集合,否则将相同字母 A 的数据放在一起(cityType.city.push(value))。


  1. 遍历到最后一条数据时,需要对最后一条数据进行处理,结束后将最后一个字母相同的数据存入城市集合。


 if (number == data.length - 1) {            if (!cityType.city.includes(value)) {              cityType.city.push(value)            }            this.cityList.push(cityType)          }
复制代码


  1. 经过以上对数据的处理,终于得到了完整的城市数据,接下来就是直接展示列表即可。


 List({ space: 14, initialIndex: 0, scroller: this.scroller }) {          ForEach(this.cityList, (bean: HotCityTypeBean, index: number) => {            if (index == 0) {              ListItem() {                Text() {                  Span("当前城市: ")                    .fontSize($r("app.float.sp_t5"))                    .fontColor($r("app.color.color_S5"))                  Span(this.cityName)                    .fontSize($r("app.float.sp_t5"))                    .fontColor($r("app.color.color_S7"))                }              }.margin({ top: $r("app.float.vp_10") })
} ListItemGroup({ header: this.headView(bean.name) }) { ForEach(bean.city, (item: HotCityBean, childIndex: number) => { ListItem() { Column() { Text(`${item.cityName}`) .height(44) .fontSize($r('app.float.sp_t4')) .width(FULL_WIDTH) .onClick(() => { }) if (childIndex == bean.city.length - 1) { Divider() .color($r('app.color.color_S3')) .height(0.5) .vertical(false) } }.alignItems(HorizontalAlign.Start) } }, (item: HotCityBean, childIndex: number) => `${childIndex}${new Date()}`) } }, (bean: HotCityTypeBean, index: number) => `${index}${new Date()}`) } .sticky(StickyStyle.Header)
复制代码


  1. 使用 ListItemGroup 用来显示每组城市的首字母,sticky(StickyStyle.Header) 则是开启粘性标题,也就是滑动列表时,每组城市的首字母固定显示在上方。

总结

本文详细讲述了对城市数据的处理,包括对音字,首字母排序以及按首字母对数据进行分组处理,特别是分组处理这块的代码比较复杂,需要亲自动手尝试才能更好的理解,学会的小伙伴赶紧动手试试吧!如果您有更好的处理方式,欢迎评论区留言!


用户头像

IT小码哥

关注

还未添加个人签名 2021-04-29 加入

还未添加个人简介

评论

发布
暂无评论
HarmonyOS实战: 城市选择功能的快速实现_HarmonyOS NEXT_IT小码哥_InfoQ写作社区