【HarmonyOS Next】地图使用详解(一)

背景


这系列文章主要讲解鸿蒙地图的使用,当前可以免费使用,并提供了丰富的SDK给开发者去自定义控件开发。目前可以实现个性化显示地图、位置搜索和路径规划等功能,轻松完成地图构建工作。需要注意的是,现在测试只能使用实体手机去做调试,模拟器和预览器是没有办法做测试和使用的

地图开发环境搭建


1. AGC中创建项目

在AGC中新建项目,并复制AGC项目中的Client ID 填写到工程中的entry模块的module.json5文件中,新增metadata,配置name为client_id,value为AGC项目中的Client ID。

typescript 复制代码
{
  "module": {
    "name": "entry",
    "type": "entry",
    "description": "$string:module_desc",
    "mainElement": "EntryAbility",
    "deviceTypes": [
      "phone",
      "tablet",
      "2in1"
    ],
    "deliveryWithInstall": true,
    "installationFree": false,
    "pages": "$profile:main_pages",
    "abilities": [
      {
        "name": "EntryAbility",
        "srcEntry": "./ets/entryability/EntryAbility.ets",
        "description": "$string:EntryAbility_desc",
        "icon": "$media:layered_image",
        "label": "$string:EntryAbility_label",
        "startWindowIcon": "$media:startIcon",
        "startWindowBackground": "$color:start_window_background",
        "exported": true,
        "skills": [
          {
            "entities": [
              "entity.system.home"
            ],
            "actions": [
              "action.system.home"
            ]
          }
        ]
      }
    ],
    "extensionAbilities": [
      {
        "name": "EntryBackupAbility",
        "srcEntry": "./ets/entrybackupability/EntryBackupAbility.ets",
        "type": "backup",
        "exported": false,
        "metadata": [
          {
            "name": "ohos.extension.backup",
            "resource": "$profile:backup_config"
          }
        ],
      }
    ],
    "metadata": [
      {
        "name": "client_id",
        "value": "6917564776665168777"
      }
    ]
  }
}

2.AGC中开通地图服务

在API管理界面,打开地图服务

3.AGC中创建APP

在证书、APP ID和Profile中,APP ID中创建之前项目中的App

4.在项目文件中生成密钥请求文件

这个密钥文件比较重要,务必妥善保存。

这里的Alias需要记住,后面需要用到

保存csr文件

查看生成的csr文件

5.AGC项目中创建证书和设备

新建调试证书,并把证书下载到本地。

6.AGC项目中创建Profile

选择对应的调试证书,完成Profile的创建

7.把生成的证书和调试文件添加到项目结构中

8.确保当前IDE已经登陆了你的华为账号

地图组件(MapComponent)

  • 示例代码使用MVVM架构来配置

1、MapComponent组件初始化

  • 提供了两个必填参数,mapOptions和mapCallback
属性名称 属性类型 属性备注 是否必填
mapOptions mapCommon.MapOptions 初始化参数,提供了地图类型、相机位置、地图边界等属性
mapCallback AsyncCallback<map.MapComponentController> 地图主要功能类map.MapComponentController的回调函数,提供了地图各种操作的API
  • 项目初始化框架
typescript 复制代码
import { MapViewModel } from '../ViewModels/MapViewModel';
import { MapComponent } from '@kit.MapKit';

@Entry
@ComponentV2
struct FirstPage {
  @Local MapVM: MapViewModel = new MapViewModel();

  aboutToAppear(): void {
    this.MapVM.Init();
  }

  build() {
    RelativeContainer() {
      MapComponent({
        mapOptions: this.MapVM.MapOption,
        mapCallback: this.MapVM.MapCallBack
      })
        .width("100%")
        .height("100%")
        .id("Map")
    }
    .height('100%')
    .width('100%')
  }
}
  • VM中的MapOption赋值
ts 复制代码
this.MapOption = {
  //相机位置
  position: {
    target: {
      latitude: this.LocationLatitude,
      longitude: this.LocationLongitude
    },
    zoom: 10
  },
  //地图类型
  mapType: mapCommon.MapType.STANDARD,
  //地图最小图层,默认值为2
  minZoom: 2,
  //地图最大图层,默认值为20
  maxZoom: 20,
  //是否支持旋转手势
  rotateGesturesEnabled: true,
  //是否支持滑动手势
  scrollGesturesEnabled: true,
  //是否支持缩放手势
  zoomGesturesEnabled: true,
  //是否支持倾斜手势
  tiltGesturesEnabled: true,
  //是否展示缩放控件
  zoomControlsEnabled: true,
  //是否展示定位按钮
  myLocationControlsEnabled: true,
  //是否展示指南针控件
  compassControlsEnabled: false,
  //是否展示比例尺
  scaleControlsEnabled: true,
  //是否一直显示比例尺,只有比例尺启用时该参数才生效。
  alwaysShowScaleEnabled: true
};
  • VM中的MapCallBack赋值
ts 复制代码
this.MapCallBack = async (err, mapController) => {
  if (!err) {
    this.MapController = mapController;
    //启用我的位置图层
    mapController.setMyLocationEnabled(true);
    //设置我的位置跟随设备移动
    mapController.setMyLocationStyle({
      displayType: mapCommon.MyLocationDisplayType.LOCATE
    })
    //启用我的位置按钮
    mapController.setMyLocationControlsEnabled(true);
    //地图监听时间管理器
    this.MapEventManager = this.MapController.getEventManager();
  }
}
  • 权限申请(在VM中封装申请)
ts 复制代码
/**
 * 所需要得权限
 */
MapPermissions: Permissions[] =
  [
    'ohos.permission.INTERNET',
    'ohos.permission.LOCATION',
    'ohos.permission.APPROXIMATELY_LOCATION'
  ]
typescript 复制代码
/**
 * 初始化方法
 */
public async Init() {
  //识别权限是否赋予
  if (!PermissionUtils.CheckPermissions(this.MapPermissions)) {
    const perResult: boolean = await PermissionUtils.RequestPermissions(this.MapPermissions);
    if (!perResult) {
      return;
    }
  }
}

权限方法封装

typescript 复制代码
import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';

/**
 *权限封装类
 */
export class PermissionUtils {
  /**
   * 检查权限是否授权(完全授权)
   * @param permissionsArr 权限集合
   * @returns true:已经全部授权;false:没有全部授权
   */
  public static CheckPermissions(permissionsArr: Permissions[]): boolean {
    const atManager = abilityAccessCtrl.createAtManager();
    //获取bundle信息
    const bundleInfo =
      bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    // 拿到当前应用的tokenID 标识
    const tokenID = bundleInfo.appInfo.accessTokenId
    //校验应用是否被授予权限
    let result: boolean = true;
    permissionsArr.forEach((x: Permissions, index: number) => {
      if (!(atManager.checkAccessTokenSync(tokenID, x) === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)) {
        result = false;
        return;
      }
    })
    return result;
  }

  /**
   * 申请授权(首次弹窗申请)
   * @param permissionList 权限集合
   * @returns true:权限完全加载;false:有权限没有加载
   */
  public static async RequestPermissions(permissionList: Permissions[]): Promise<boolean> {
    // 程序访问控制管理
    const atManager = abilityAccessCtrl.createAtManager();
    // 拉起弹框请求用户授权
    const grantStatus = await atManager.requestPermissionsFromUser(getContext(), permissionList)
    // 获取请求权限的结果
    const isAuth = grantStatus.authResults.every(v => v === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
    // 返回 Promise 授权结果
    return isAuth ? Promise.resolve(true) : Promise.reject(false)
  }

  /**
   * 打开系统设置的权限管理页面
   */
  public static 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
      }
    })
  }
}
  • 项目加载时,先进行VM的初始化
typescript 复制代码
aboutToAppear(): void {
  this.MapVM.Init();
}
  • 界面初始化展示

2、地图初始化类(MapOptions)

属于mapCommon类

  • 常用属性
名称 类型 可选 说明
mapType MapType 地图类型,默认值为MapType.STANDARD,异常值按默认值处理。
position CameraPosition 地图相机位置。
bounds LatLngBounds 地图展示边界,异常值根据无边界处理。说明西南角纬度不能大于东北角纬度。
minZoom number 地图最小图层,有效范围:[2, 20],默认值为2,异常值按默认值处理。如果设置的最小缩放级别小于2,minZoom会取2。
maxZoom number 地图最大图层,有效范围:[2, 20],默认值为20,异常值按默认值处理。如果设置的最大缩放级别大于20,maxZoom会取20。
rotateGesturesEnabled boolean 是否支持旋转手势,默认值为true,异常值按默认值处理。true:支持false:不支持
scrollGesturesEnabled boolean 是否支持滑动手势,默认值为true,异常值按默认值处理。true:支持false:不支持
zoomGesturesEnabled boolean 是否支持缩放手势,默认值为true,异常值按默认值处理。true:支持false:不支持
tiltGesturesEnabled boolean 是否支持倾斜手势,默认值为true,异常值按默认值处理。true:支持false:不支持
zoomControlsEnabled boolean 是否展示缩放控件,默认值为true,异常值按默认值处理。true:展示false:不展示
myLocationControlsEnabled boolean 是否展示定位按钮,默认值为false,异常值按默认值处理。true:展示false:不展示
compassControlsEnabled boolean 是否展示指南针控件,默认值为true,异常值按默认值处理。true:展示false:不展示
scaleControlsEnabled boolean 是否展示比例尺,默认值为false,异常值按默认值处理。true:展示false:不展示
padding Padding 设置地图和边界的距离,默认值为{ left: 0 , top: 0 , right: 0 , bottom: 0 }。
styleId string 自定义样式ID。
dayNightMode DayNightMode 日间夜间模式,默认值为DayNightMode.DAY(日间模式)
alwaysShowScaleEnabled boolean 是否一直显示比例尺,只有比例尺启用时该参数才生效。
  • 在VM类中,需要在类初始化的时候把MapOption初始化。
ts 复制代码
constructor() {
  this.MapOption = {
    //相机位置
    position: {
      target: {
        latitude: this.LocationLatitude,
        longitude: this.LocationLongitude
      },
      zoom: 10
    },
    //地图类型
    mapType: mapCommon.MapType.STANDARD,
    //地图最小图层,默认值为2
    minZoom: 2,
    //地图最大图层,默认值为20
    maxZoom: 20,
    //是否支持旋转手势
    rotateGesturesEnabled: true,
    //是否支持滑动手势
    scrollGesturesEnabled: true,
    //是否支持缩放手势
    zoomGesturesEnabled: true,
    //是否支持倾斜手势
    tiltGesturesEnabled: true,
    //是否展示缩放控件
    zoomControlsEnabled: true,
    //是否展示定位按钮
    myLocationControlsEnabled: true,
    //是否展示指南针控件
    compassControlsEnabled: false,
    //是否展示比例尺
    scaleControlsEnabled: true,
    //是否一直显示比例尺,只有比例尺启用时该参数才生效。
    alwaysShowScaleEnabled: true
  };
  this.MapCallBack = async (err, mapController) => {
    if (!err) {
      this.MapController = mapController;
      //启用我的位置图层
      mapController.setMyLocationEnabled(true);
      //设置我的位置跟随设备移动
      mapController.setMyLocationStyle({
        displayType: mapCommon.MyLocationDisplayType.LOCATE
      })
      //启用我的位置按钮
      mapController.setMyLocationControlsEnabled(true);
      //地图监听时间管理器
      this.MapEventManager = this.MapController.getEventManager();
    }
  }
}

3、获取手机用户当前位置

通过geoLocationManager的getCurrentLocation方法,获取用户的坐标经纬度,然后封装成更新用户定位的方法,在初始化的时候调用,就可以实现手机打开后会直接更新到用户的位置

typescript 复制代码
/**
 * 更新用户定位
 */
public async UpdateLocation() {
  // 获取用户位置坐标
  let location: geoLocationManager.Location = await geoLocationManager.getCurrentLocation();
  this.LocationLongitude = location.longitude;
  this.LocationLatitude = location.latitude;
}

完整代码

  • Page
typescript 复制代码
import { MapViewModel } from '../ViewModels/MapViewModel';
import { MapComponent } from '@kit.MapKit';

@Entry
@ComponentV2
struct FirstPage {
  @Local MapVM: MapViewModel = new MapViewModel();

  aboutToAppear(): void {
    this.MapVM.Init();
  }

  build() {
    RelativeContainer() {
      MapComponent({
        mapOptions: this.MapVM.MapOption,
        mapCallback: this.MapVM.MapCallBack
      })
        .width("100%")
        .height("100%")
        .id("Map")
    }
    .height('100%')
    .width('100%')
  }
}
  • ViewModel
typescript 复制代码
import { mapCommon, map, sceneMap } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
import { common, Permissions } from '@kit.AbilityKit';
import { PermissionUtils } from '../Utils/PermissionUtils';
import geoLocationManager from '@ohos.geoLocationManager';
import { image } from '@kit.ImageKit';
import { photoAccessHelper } from '@kit.MediaLibraryKit';
import { fileIo } from '@kit.CoreFileKit';
import { MapMarkImage } from '../Utils/MapMarkImage';

@ObservedV2
export class MapViewModel {
  /**
   * 地图初始化参数设置
   */
  MapOption?: mapCommon.MapOptions
  /**
   * 地图回调方法
   */
  MapCallBack?: AsyncCallback<map.MapComponentController>
  /**
   * 地图控制器
   */
  MapController?: map.MapComponentController
  /**
   * 地图监听管理器
   */
  MapEventManager?: map.MapEventManager
  /**
   * 地图标记集合
   */
  Markers: map.Marker[] = []
  /**
   * 所需要得权限
   */
  MapPermissions: Permissions[] =
    [
      'ohos.permission.INTERNET',
      'ohos.permission.LOCATION',
      'ohos.permission.APPROXIMATELY_LOCATION'
    ]
  /**
   * 当前位置的维度
   */
  public LocationLatitude: number = 39.9;
  /**
   * 当前位置的经度
   */
  public LocationLongitude: number = 116.4;

  /**
   * 初始化方法
   */
  public async Init() {
    //识别权限是否赋予
    if (!PermissionUtils.CheckPermissions(this.MapPermissions)) {
      const perResult: boolean = await PermissionUtils.RequestPermissions(this.MapPermissions);
      if (!perResult) {
        return;
      }
    }
    //标点初始化
    MapMarkImage.Init(getContext(this));
  }

  constructor() {
    this.UpdateLocation();
    this.MapOption = {
      //相机位置
      position: {
        target: {
          latitude: this.LocationLatitude,
          longitude: this.LocationLongitude
        },
        zoom: 10
      },
      //地图类型
      mapType: mapCommon.MapType.STANDARD,
      //地图最小图层,默认值为2
      minZoom: 2,
      //地图最大图层,默认值为20
      maxZoom: 20,
      //是否支持旋转手势
      rotateGesturesEnabled: true,
      //是否支持滑动手势
      scrollGesturesEnabled: true,
      //是否支持缩放手势
      zoomGesturesEnabled: true,
      //是否支持倾斜手势
      tiltGesturesEnabled: true,
      //是否展示缩放控件
      zoomControlsEnabled: true,
      //是否展示定位按钮
      myLocationControlsEnabled: true,
      //是否展示指南针控件
      compassControlsEnabled: false,
      //是否展示比例尺
      scaleControlsEnabled: true,
      //是否一直显示比例尺,只有比例尺启用时该参数才生效。
      alwaysShowScaleEnabled: true
    };
    this.MapCallBack = async (err, mapController) => {
      if (!err) {
        this.MapController = mapController;
        //启用我的位置图层
        mapController.setMyLocationEnabled(true);
        //设置我的位置跟随设备移动
        mapController.setMyLocationStyle({
          displayType: mapCommon.MyLocationDisplayType.LOCATE
        })
        //启用我的位置按钮
        mapController.setMyLocationControlsEnabled(true);
        //地图监听时间管理器
        this.MapEventManager = this.MapController.getEventManager();
      }
    }
  }

  /**
   * 更新用户定位
   */
  public async UpdateLocation() {
    // 获取用户位置坐标
    let location: geoLocationManager.Location = await geoLocationManager.getCurrentLocation();
    this.LocationLongitude = location.longitude;
    this.LocationLatitude = location.latitude;
  }

  /**
   * 移动视图相机
   * @param latitude 维度
   * @param longitude 经度
   */
  public async MoveCamera(latitude: number, longitude: number) {
    if (this.MapController) {
      //将视图移动到标点位置
      let nwPosition = map.newCameraPosition({
        target: {
          latitude: latitude,
          longitude: longitude
        },
        zoom: 10
      })
      // 以动画方式移动地图相机
      this.MapController.animateCamera(nwPosition, 1000);
    }
  }
}
  • PermissionUtils
typescript 复制代码
import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';

/**
 *权限封装类
 */
export class PermissionUtils {
  /**
   * 检查权限是否授权(完全授权)
   * @param permissionsArr 权限集合
   * @returns true:已经全部授权;false:没有全部授权
   */
  public static CheckPermissions(permissionsArr: Permissions[]): boolean {
    const atManager = abilityAccessCtrl.createAtManager();
    //获取bundle信息
    const bundleInfo =
      bundleManager.getBundleInfoForSelfSync(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
    // 拿到当前应用的tokenID 标识
    const tokenID = bundleInfo.appInfo.accessTokenId
    //校验应用是否被授予权限
    let result: boolean = true;
    permissionsArr.forEach((x: Permissions, index: number) => {
      if (!(atManager.checkAccessTokenSync(tokenID, x) === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)) {
        result = false;
        return;
      }
    })
    return result;
  }

  /**
   * 申请授权(首次弹窗申请)
   * @param permissionList 权限集合
   * @returns true:权限完全加载;false:有权限没有加载
   */
  public static async RequestPermissions(permissionList: Permissions[]): Promise<boolean> {
    // 程序访问控制管理
    const atManager = abilityAccessCtrl.createAtManager();
    // 拉起弹框请求用户授权
    const grantStatus = await atManager.requestPermissionsFromUser(getContext(), permissionList)
    // 获取请求权限的结果
    const isAuth = grantStatus.authResults.every(v => v === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED)
    // 返回 Promise 授权结果
    return isAuth ? Promise.resolve(true) : Promise.reject(false)
  }

  /**
   * 打开系统设置的权限管理页面
   */
  public static 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
      }
    })
  }
}

总结

上面的流程是地图组件的初始化的个人理解流程,看完这篇希望可以在地图开发上给你提供帮助。

相关推荐
seabirdssss2 小时前
华为机试牛客刷题之HJ75 公共子串计算
java·算法·华为
关山月2 小时前
鸿蒙开发者高级认证-理论考试题(2025年02月)第一弹
harmonyos
东林知识库2 小时前
鸿蒙NEXT开发-用户通知服务
华为·harmonyos
HarmonyOS_SDK4 小时前
【FAQ】HarmonyOS SDK 闭源开放能力 —Push Kit(9)
harmonyos
png5 小时前
从零开始纯血鸿蒙天气预报-城市管理页面(1)
harmonyos·arkui
SuperHeroWu77 小时前
【HarmonyOS Next】鸿蒙状态管理V2装饰器详解
华为·harmonyos·装饰器·状态管理·v2·v1
武汉誉天8 小时前
华为数通Datacom认证体系详解:从HCIA到HCIE的进阶路径
华为