关于华为地图在鸿蒙应用中的使用与实现

举报
yd_241646257 发表于 2025/06/04 19:58:23 2025/06/04
【摘要】 华为地图(Huawei Map Kit)作为鸿蒙系统生态中的重要组件,为开发者提供了丰富的地理信息服务与交互能力,在鸿蒙应用中集成华为地图不仅能够提升用户体验,还能通过地图功能增强社交属性,实现人与人、人与地点之间的高效连接。

华为地图(Huawei Map Kit)作为鸿蒙系统生态中的重要组件,为开发者提供了丰富的地理信息服务与交互能力,在鸿蒙应用中集成华为地图不仅能够提升用户体验,还能通过地图功能增强社交属性,实现人与人、人与地点之间的高效连接。华为地图作为 HMS Core 的一部分,通过 SDK 提供给开发者使用,在鸿蒙应用中可以通过调用 Map Kit 提供的 API 实现地图加载、定位、标记、路径规划等功能;由于鸿蒙系统的分布式特性,地图服务可以在不同设备间无缝流转,例如手机、平板、智能穿戴设备等,支持2D/3D地图展示提供多种地图类型如卫星图、地形图,结合华为定位服务(Location Kit)实现高精度位置获取,用户可搜索周边POI(Point of Interest),如餐厅、商场、加油站等,提供步行、骑行、驾车等多种出行方式的路线建议,开发者可添加个性化图标、热力图、轨迹线等信息,这些功能构成了一个强大的地图平台,为社交类应用提供了坚实的技术基础。

在社交应用中,地图不仅仅是导航工具,它扮演着连接人与人、人与地点的重要角色,基于用户的实时位置推荐附近的社交对象或活动场所促进线下互动,用户可以将自己的位置实时共享给好友便于见面、约会或组队活动,用户可在特定地点签到形成社交动态增加互动性与趣味性,这种基于地理位置的社交模式打破了传统社交仅靠文字和图片交流的局限增强了真实感与参与度;在组织线下聚会、旅游、运动等活动时,地图可以发挥重要作用,通过地图标注活动地点让用户一目了然地了解集合点、路线、目标位置,多个参与者的位置可以在一张地图上集中展示方便组织者掌握整体情况,成员之间可以共享导航路径确保大家能顺利到达目的地,这些功能使得社交活动更加有序、高效,提升了用户体验;地图可以作为兴趣图谱的基础载体帮助用户发现志同道合的朋友,用户可以在地图上的某个地点发布评论、照片或视频形成社交内容吸引其他用户互动,通过地图热力图识别出热门社交区域或活动聚集地引导用户前往参与,结合用户的常去地点与兴趣标签推荐附近可能感兴趣的社交圈子或个人,这种方式让社交从“线上虚拟”走向“线下现实”,提升了社交的真实性和粘性;地图还成为本地化内容传播的媒介,用户可在地图上查看本地的活动、促销、演出等信息形成“社交+生活”的信息流,用户上传的照片、视频等内容可以自动绑定地理位置形成可视化的社交足迹,用户可以创建包含多个地点的“地图故事”记录旅行、探险等经历并与好友分享,这不仅丰富了社交内容的表现形式也增强了用户对空间的感知与记忆。

华为地图在鸿蒙应用中的集成不仅是技术层面的赋能更是社交体验升级的关键推动力,通过地图用户得以在真实世界中找到彼此、发现兴趣、组织活动从而构建更具温度和深度的社交关系,未来随着鸿蒙系统的进一步发展与地图能力的持续优化地图将在更多元化的社交场景中发挥核心作用推动“人—地—事”三位一体的社交新形态的形成,典型的应用案例包括如交友、约会类应用利用地图进行附近人查找、实时位置共享、安全护送等功能增强信任感与安全性,跑步、骑行社群中地图用于记录运动轨迹、展示排行榜、组织挑战赛等提升用户参与感与成就感,用户可通过地图标记旅行路线、分享景点体验形成“地图日记”并与好友互动点评,整合地图与社交元素用户可在地图上发现美食、娱乐场所并看到好友评价提升决策效率与社交影响力。

相关代码如下:

import { MapComponent, mapCommon, map } from '@kit.MapKit';
import { AsyncCallback, BusinessError, request } from '@kit.BasicServicesKit';
import { geoLocationManager } from '@kit.LocationKit';
import { Map } from '../../entity/Map';
import fs from '@ohos.file.fs';
import { buffer, util } from '@kit.ArkTS';
import { image } from '@kit.ImageKit';
import { http } from '@kit.NetworkKit';
import fileIo from '@ohos.file.fs';
import { fileUri } from '@kit.CoreFileKit';
import { MemberCount } from '../../entity/MemberCount';
import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit'; // 导入权限管理相关的库
import { permissionManager } from '../../manager';
import { getLocation } from '../../common/Common';

@Component
export default struct mapPage {
  private TAG = "HuaweiMapDemo";
  private mapOptions?: mapCommon.MapOptions;
  private callback?: AsyncCallback<map.MapComponentController>;
  private mapController?: map.MapComponentController;
  private mapEventManager?: map.MapEventManager;
  private marker?: map.Marker;
  @StorageProp('mapInfo') mapInfo: Map[] = []
  @StorageProp('MemberCount') MemberCount: MemberCount | undefined = undefined
  @StorageProp('location') location: geoLocationManager.Location | undefined = undefined
  // @State location: geoLocationManager.Location | undefined = undefined
  // 需要的权限
  requiredPermissions: Array<Permissions> = ['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION',];
  //检查权限
  checkPermission(): boolean{
    return permissionManager.checkPermissions([
      'ohos.permission.ACCESS_BLUETOOTH',
    ])
  }
  // 请求用户权限
  requestPermissionsFromUser() {
    const context = getContext(this) as common.UIAbilityContext; // 获取上下文
    const atManager = abilityAccessCtrl.createAtManager(); // 创建权限管理器
    // 请求权限
    atManager.requestPermissionsFromUser(context, this.requiredPermissions, (err, data) => {
      const grantStatus: Array<number> = data.authResults; // 获取授权结果
      if (grantStatus.toString() == "-1") { // 用户拒绝权限
        this.showAlertDialog(); // 显示提示对话框
      } else if (grantStatus.toString() == "0") { // 用户同意权限

      }
    });
  }
  // 显示对话框提示用户开启权限
  showAlertDialog() {
    this.getUIContext().showAlertDialog({
      autoCancel: true, // 自动取消
      title: '权限申请', // 对话框标题
      message: '如需使用此功能,请前往设置页面开启位置权限。', // 对话框消息
      cancel: () => {
      },
      confirm: {
        defaultFocus: true, // 默认聚焦确认按钮
        value: '好的', // 确认按钮文本
        action: () => {
          this.openPermissionSettingsPage(); // 打开权限设置页面
        }
      },
      onWillDismiss: () => {
      },
      alignment: DialogAlignment.Center, // 对话框对齐方式
    });
  }
  // 打开权限设置页面
  openPermissionSettingsPage() {
    const context = getContext() as common.UIAbilityContext; // 获取上下文
    const bundleInfo = bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION); // 获取包信息
    context.startAbility({
      bundleName: 'com.huawei.hmos.settings', // 设置页面的包名
      abilityName: 'com.huawei.hmos.settings.MainAbility', // 设置页面的能力名
      uri: 'application_info_entry', // 打开设置->应用和元服务
      parameters: {
        pushParams: bundleInfo.name // 按照包名打开对应设置页
      }
    });
  }

  async imageUrlToPixelMap(url: string) {
    return new Promise<PixelMap>((resolve, reject) => {
      http.createHttp().request(
        url,
        { expectDataType: http.HttpDataType.ARRAY_BUFFER }
      ).then(async (res) => {
        console.info('res', JSON.stringify(res))
        // 将图片资源转为像素图(PixelMap)
        const map = await image.createImageSource(res.result as ArrayBuffer).createPixelMap()
        resolve(map)
      }).catch((e: BusinessError) => {
        reject(e)
      })
    })
  }

  downloadImageWithUrl(url: string, completion: (imageData: ArrayBuffer) => void) {
    http.createHttp().request(url,
      (error: BusinessError, data: http.HttpResponse) => {
        if (error || data.responseCode != 200) {
          return
        }
        completion((data.result as ArrayBuffer))
      })
  }
  async aboutToAppear(): Promise<void> {
    // 地图初始化参数,设置地图中心点坐标及层级
    this.mapOptions = {
      position: {
        target: {
          latitude: this.location ? this.location.latitude : 35.674895,
          longitude: this.location ? this.location.longitude : 110.098818
        },
        zoom: 4
      },
      myLocationControlsEnabled: true,
      scaleControlsEnabled: true
    };
    // 地图初始化的回调
    this.callback = async (err, mapController) => {
      if (!err) {
        // 获取地图的控制器类,用来操作地图
        this.mapController = mapController;
        this.mapEventManager = this.mapController.getEventManager();
        // 启用我的位置图层
        this.mapController.setMyLocationEnabled(true);
        // 启用我的位置按钮
        this.mapController.setMyLocationControlsEnabled(true);
        // Marker初始化参数
        for (const element of this.mapInfo) {
          // 创建 MarkerOptions
          let markerOptions: mapCommon.MarkerOptions = {
            position: {
              latitude: Number(element.latitude),
              longitude: Number(element.longitude),
            },
            rotation: 0,
            visible: true,
            zIndex: 0,
            alpha: 1,
            anchorU: 0.5,
            anchorV: 1,
            clickable: true,
            draggable: false,
            flat: false,
            icon: 'defaultPic.png', // 使用下载的图片作为 Marker 图标
          };
          // 创建 Marker
          this.marker = await this.mapController.addMarker(markerOptions);
          // 设置信息窗的标题
          this.marker.setTitle(element.headPicUrl + "*" + element.currentStatus + "*" + element.nickName + '*' + element.createDate);
        }
        this.mapEventManager.on("mapLoad", () => {
          console.info(this.TAG, `on-mapLoad`);
        });
      }
    };
  }

  // 页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效
  onPageShow(): void {
    console.log('test-map-onpageshow')
    // 将地图切换到前台
    if (this.mapController !== undefined) {
      this.mapController.show();
    }
  }

  // 页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效。
  onPageHide(): void {
    // 将地图切换到后台
    if (this.mapController !== undefined) {
      this.mapController.hide();
    }
  }

  build() {
    Stack() {
      Column() {
        MapComponent({
          mapOptions: this.mapOptions,
          mapCallback: this.callback,
          // 自定义信息窗
          customInfoWindow: this.customInfoWindow
        })
          .width('100%')
          .height('100%');
      }.width('100%')

      Column({ space: 7 }) {
        Row({ space: 12 }) {
          Text($r('app.string.zhucezongshu')).fontSize(14).fontWeight(FontWeight.Bold).fontColor(Color.Black)
          Text(this.MemberCount?.memberCount.toString()).fontSize(14).fontColor(Color.Black)
        }

        Row({ space: 12 }) {
          Text($r('app.string.yonghudongtaizongshu')).fontSize(14).fontWeight(FontWeight.Bold).fontColor(Color.Black)
          Text(this.MemberCount?.memberDynamicsCount.toString()).fontSize(14).fontColor(Color.Black)
        }

        Row({ space: 12 }) {
          Text($r('app.string.jinriyonghudongtaishu')).fontSize(14).fontWeight(FontWeight.Bold).fontColor(Color.Black)
          Text(this.MemberCount?.memberTodayDynamicsCount.toString()).fontSize(14).fontColor(Color.Black)
        }
      }
      .position({ top: '1%', right: '2.5%' })
      .zIndex(1)
      .margin({ top: 25 })
      .padding({ right: 20 })

    }.height('100%').width('100%')
  }

  // 自定义信息窗BuilderParam
  @BuilderParam customInfoWindow: ($$: map.MarkerDelegate) => void = this.customInfoWindowBuilder;

  // 自定义信息窗Builder
  @Builder
  customInfoWindowBuilder($$: map.MarkerDelegate) {
    if ($$.marker) {
      Row({ space: 7 }) {
        Image(getPart($$.marker.getTitle(), 0) != 'null' ? getPart($$.marker.getTitle(), 0) :
        $rawfile('defaultPic.png'))
          .height(85)
          .width(85)
          .clipShape(new Circle({ width: 85, height: 85 }))
        Column() {
          Text(hidePhoneNumber(getPart($$.marker.getTitle(), 2))).width('100%').fontColor("#6DD0EC")
          Row() {
            if (getPart($$.marker.getTitle(), 1) == "开心") {
              Image($r('app.media.01_e_fill'))
                .size({ height: 20, width: 20 })
            }
            if (getPart($$.marker.getTitle(), 1) == "难过") {
              Image($r('app.media.01_q_fill')).size({ height: 20, width: 20 })
            }
            if (getPart($$.marker.getTitle(), 1) == "一般") {
              Image($r('app.media.01_w_fill')).size({ height: 20, width: 20 })
            }
          }.width('100%').justifyContent(FlexAlign.Start)

          Text(timeAgo(getPart($$.marker.getTitle(), 3))).width('100%').fontColor(Color.White)
        }
        .justifyContent(FlexAlign.SpaceAround)
        .width(80)
        .height(85)
      }
      .padding(5)
      .borderRadius(10)
      .backgroundColor("#233C74")

    }
  }
}


function hidePhoneNumber(input: string): string {
  const phoneRegex = /^\d{11}$/; // 匹配11位数字的手机号码
  if (phoneRegex.test(input)) {
    return input.substring(0, 3) + '****' + input.substring(7);
  }
  return input; // 不是电话号码,返回原字符串
}

function timeAgo(dateString: string): string {
  const now = new Date();
  const date = new Date(dateString);
  const seconds = Math.floor((now.getTime() - date.getTime()) / 1000);

  let interval = Math.floor(seconds / 31536000); // 年
  if (interval > 1) {
    return `${interval}年前`;
  }
  interval = Math.floor(seconds / 2592000); // 月
  if (interval > 1) {
    return `${interval}个月前`;
  }
  interval = Math.floor(seconds / 604800); // 周
  if (interval > 1) {
    return `${interval}周前`;
  }
  interval = Math.floor(seconds / 86400); // 天
  if (interval > 1) {
    return `${interval}天前`;
  }
  interval = Math.floor(seconds / 3600); // 小时
  if (interval > 1) {
    return `${interval}小时前`;
  }
  interval = Math.floor(seconds / 60); // 分钟
  if (interval > 1) {
    return `${interval}分钟前`;
  }
  return '刚刚';
}

function getPart(input: string, num: number): string {
  return input.split('*')[num]; // 获取第一部分
}
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。