在 HarmonyOS 应用开发中,地图功能是本地生活、出行导航、位置服务类 App 的核心模块。华为 Map Kit 提供了丰富的地图能力,包括地图渲染、标记点管理、相机控制、定位融合等功能。本文将从开发准备(签名配置) 到核心功能实现(地图显示、标记控制、定位融合) ,一步步拆解 Map Kit 的集成流程,附带完整可复用代码,助力开发者快速落地地图相关功能。
签名
签名的主要原因是为了确保应用的安全性和合法性,HarmonyOS应用/元服务通过数字证书(.cer文件)和Profile文件(.p7b文件)来保证应用/元服务的完整性。
编辑
- 准备:创建项目、创建应用
- 菜单【证书、APP ID和Profile】点击APP ID 然后点击新增
```
应用名称: app名字 例如我写 锋密手持弹幕 或者 测试1
应用包名 com.xxx.xxx com.sljz.tet1 一般都是网址倒过来写 如果元服务就不用写了
```
- 1.生成秘钥和证书请求文件:Build构建 => generate key and csr 生成私钥和证书请求文件
genderate key fmcrm.p12 秘钥密码123456.z
generator csr fmcrm.csr
- 2.申请发布证书
https://developer.huawei.com/consumer/cn/ 点击右侧管理中心 =》 点击【appgallery connect】 =》点击【证书、APP ID和Profile 】 => 点击左侧菜单【证书】=》点击新增证书
证书名称: fmcrm_release_cert
证书类型: 发布证书
证书请求文件:fmcrm.csr
- 3.申请发布Profile:点击【证书、APP ID和Profile】新增【Profile】名字【fmcrm_profile_】下载后就是fmcrm_profile_Release.p7b文件
名称: fmscdm_profile_ 后期下载就是 fmscdm_profile_Release.p7b
类型:发布
证书 2操作的
权限:不用
- 4.配置签名信息 File -> Project Structure... => signing config
default : bundle name 默认的 然后去掉 auto 勾选
填写 ok就行
注:如果修改的不是default 则要继续修改`根目录build-profile.json5文件` =》 `products` => `"signingConfig": "app1自己写的名字",`
显示地图
developer.huawei.com/consumer/cn…
import { MapComponent, mapCommon, map } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
@Entry
@Component
struct HuaweiMapDemo {
private TAG = "HuaweiMapDemo";
private mapOptions?: mapCommon.MapOptions;
private callback?: AsyncCallback<map.MapComponentController>;
private mapController?: map.MapComponentController;
private mapEventManager?: map.MapEventManager;
aboutToAppear(): void {
// 地图初始化参数,设置地图中心点坐标及层级
this.mapOptions = {
position: {
target: {
latitude: 39.9,
longitude: 116.4
},
zoom: 10
}
};
// 地图初始化的回调
this.callback = async (err, mapController) => {
if (!err) {
// 获取地图的控制器类,用来操作地图
this.mapController = mapController;
this.mapEventManager = this.mapController.getEventManager();
let callback = () => {
console.info(this.TAG, `on-mapLoad`);
}
this.mapEventManager.on("mapLoad", callback);
}
};
}
// 页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效
onPageShow(): void {
// 将地图切换到前台
if (this.mapController) {
this.mapController.show();
}
}
// 页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效
onPageHide(): void {
// 将地图切换到后台
if (this.mapController) {
this.mapController.hide();
}
}
build() {
Stack() {
// 调用MapComponent组件初始化地图
MapComponent({ mapOptions: this.mapOptions, mapCallback: this.callback }).width('100%').height('100%');
}.height('100%')
}
}
控制地图标记
编辑
import { MapComponent, mapCommon, map } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
@Entry
@Component
struct HuaweiMapDemo {
private TAG = "HuaweiMapDemo";
private mapOptions?: mapCommon.MapOptions;
private callback?: AsyncCallback<map.MapComponentController>;
private mapController?: map.MapComponentController;
private mapEventManager?: map.MapEventManager;
private marker?: map.Marker;
aboutToAppear(): void {
// 地图初始化参数,设置地图中心点坐标及层级
this.mapOptions = {
position: {
target: {
latitude: 39.9,
longitude: 116.4
},
zoom: 10
}
};
// 地图初始化的回调
this.callback = async (err, mapController) => {
if (!err) {
// 获取地图的控制器类,用来操作地图
this.mapController = mapController;
this.mapEventManager = this.mapController.getEventManager();
let callback = async () => {
console.info(this.TAG, `on-mapLoad`);
let markerOptions: mapCommon.MarkerOptions = {
position: {
latitude: 39.9,
longitude: 116.4
},
icon: $r('app.media.point'), // ✅ update1
clickable: true,
// 设置信息窗标题
title: "自定义信息窗", // ✅ update1
};
this.marker = await this.mapController?.addMarker(markerOptions);
this.marker?.setInfoWindowVisible(true);
this.marker?.setSnippet('这是地址');
}
this.mapEventManager.on("mapLoad", callback);
}
};
}
// 页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效
onPageShow(): void {
// 将地图切换到前台
if (this.mapController) {
this.mapController.show();
}
}
// 页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效
onPageHide(): void {
// 将地图切换到后台
if (this.mapController) {
this.mapController.hide();
}
}
build() {
Stack() {
// 调用MapComponent组件初始化地图
MapComponent({ mapOptions: this.mapOptions, mapCallback: this.callback,
// 自定义信息窗 // ✅ update2
customInfoWindow: this.customInfoWindow }).width('100%').height('100%');
}.height('100%')
}
// ✅ update2
// ✅ update2
@BuilderParam customInfoWindow: (params: map.MarkerDelegate) => void = this.customInfoWindowBuilder;
@Builder
customInfoWindowBuilder(params: map.MarkerDelegate) {
if (params.marker) {
Column() {
Text(params.marker.getTitle())
.width(180)
.height(24)
.backgroundColor(Color.Green)
.borderRadius({
topLeft: 12,
topRight: 12
})
.padding({
top: 4,
bottom: 4,
left: 12
})
.fontSize(12)
.fontColor('#E6FFFFFF')
.lineHeight(16)
.textAlign(TextAlign.Start)
Text(params.marker.getSnippet())
.width(180)
.backgroundColor('#FFFFFF')
.borderRadius({
bottomLeft: 12,
bottomRight: 12
})
.padding({
top: 4,
bottom: 6,
left: 12,
right: 8
})
.fontSize(14)
.fontColor('#E6000000')
.lineHeight(19)
.textAlign(TextAlign.Start)
}
}
}
}
更改地图位置
developer.huawei.com/consumer/cn…
Button('测试').onClick(() => {
console.log('hello')
this.marker?.setPosition({
latitude: 29.9,
longitude: 116.4
});
this.marker?.setInfoWindowVisible(true);
this.marker?.setSnippet('这是地址2');
let mapLocation: mapCommon.LatLng = {
latitude: 29.9,
longitude: 116.4
}
// await map.convertCoordinate(mapCommon.CoordinateType.WGS84, mapCommon.CoordinateType.GCJ02, {
// latitude: location.latitude,
// longitude: location.longitude
// });
let cameraUpdate = map.newCameraPosition( { // 创建CameraUpdate对象
target: mapLocation,
zoom: 15,
tilt: 0,
bearing: 0
});
this.mapController?.animateCamera(cameraUpdate, 100); // // 以动画方式移动地图相机
// ....
})
定位
封装Map组件
import { MapComponent, mapCommon, map } from '@kit.MapKit';
import { AsyncCallback } from '@kit.BasicServicesKit';
@Component
export struct Map2 {
@Consume latitude:number
@Consume longitude:number
@Consume @Watch('onUploadLocation') content:string
async onUploadLocation() {
console.log('slj Map2 onUploadLocation ', this.latitude)
console.log('slj Map2 onUploadLocation ', this.longitude)
console.log('slj Map2 onUploadLocation ', this.content)
// FIXBUG: 定位获取的经度纬度 直接给华为地图不够精确 需要通过转换下
let mapLocation: mapCommon.LatLng = await map.convertCoordinate(mapCommon.CoordinateType.WGS84, mapCommon.CoordinateType.GCJ02, {
latitude: this.latitude,
longitude: this.longitude
});
this.marker?.setPosition(mapLocation)
this.marker?.setSnippet(this.content);
this.marker?.setInfoWindowVisible(true);
let cameraUpdate = map.newCameraPosition( { // 创建CameraUpdate对象
target: mapLocation,
zoom: 15,
tilt: 0,
bearing: 0
});
this.mapController?.animateCamera(cameraUpdate, 1000);
}
private TAG = "HuaweiMapDemo";
private mapOptions?: mapCommon.MapOptions;
private callback?: AsyncCallback<map.MapComponentController>;
private mapController?: map.MapComponentController;
private mapEventManager?: map.MapEventManager;
private marker?: map.Marker;
aboutToAppear(): void {
// 地图初始化参数,设置地图中心点坐标及层级
this.mapOptions = {
position: {
target: {
latitude: 39.9,
longitude: 116.4
},
zoom: 10
}
};
// 地图初始化的回调
this.callback = async (err, mapController) => {
if (!err) {
// 获取地图的控制器类,用来操作地图
this.mapController = mapController;
this.mapEventManager = this.mapController.getEventManager();
let callback = async () => {
console.info(this.TAG, `on-mapLoad`);
// ========
// Marker初始化参数
let markerOptions: mapCommon.MarkerOptions = {
position: {
latitude: 39.9,
longitude: 116.4
},
rotation: 0,
visible: true,
zIndex: 0,
alpha: 1,
anchorU: 0.5,
anchorV: 1,
clickable: true,
draggable: true,
flat: false,
icon: $r('app.media.point'), // ✅ update1
// clickable: true,
// 设置信息窗标题
title: "您当前的位置信息如下:", // ✅ update1
};
// 创建Marker
this.marker = await this.mapController?.addMarker(markerOptions);
this.marker?.setSnippet('这是地址');
this.marker?.setInfoWindowVisible(true);
// ========
}
this.mapEventManager.on("mapLoad", callback);
}
};
}
// 页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效
onPageShow(): void {
// 将地图切换到前台
if (this.mapController) {
this.mapController.show();
}
}
// 页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效
onPageHide(): void {
// 将地图切换到后台
if (this.mapController) {
this.mapController.hide();
}
}
build() {
Stack() {
// 调用MapComponent组件初始化地图
MapComponent({ mapOptions: this.mapOptions, mapCallback: this.callback,
// 自定义信息窗 // ✅ update2
customInfoWindow: this.customInfoWindow }).width('100%').height('100%');
// Button('更改位置').onClick(() => {
//
// this.marker?.setPosition({
// latitude: 29.9,
// longitude: 116.4
// })
// this.marker?.setSnippet('这是地址2');
// this.marker?.setInfoWindowVisible(true);
//
//
//
// let cameraUpdate = map.newCameraPosition( { // 创建CameraUpdate对象
// target: {
// latitude: 29.9,
// longitude: 116.4
// },
// zoom: 15,
// tilt: 0,
// bearing: 0
// });
// this.mapController?.animateCamera(cameraUpdate, 1000);
// })
}.height('100%')
}
// ✅ update2
// ✅ update2
@BuilderParam customInfoWindow: (params: map.MarkerDelegate) => void = this.customInfoWindowBuilder;
@Builder
customInfoWindowBuilder(params: map.MarkerDelegate) {
if (params.marker) {
Column() {
Text(params.marker.getTitle())
.width(180)
.height(24)
.backgroundColor(Color.Green)
.borderRadius({
topLeft: 12,
topRight: 12
})
.padding({
top: 4,
bottom: 4,
left: 12
})
.fontSize(12)
.fontColor('#E6FFFFFF')
.lineHeight(16)
.textAlign(TextAlign.Start)
Text(params.marker.getSnippet())
.width(180)
.backgroundColor('#FFFFFF')
.borderRadius({
bottomLeft: 12,
bottomRight: 12
})
.padding({
top: 4,
bottom: 6,
left: 12,
right: 8
})
.fontSize(14)
.fontColor('#E6000000')
.lineHeight(19)
.textAlign(TextAlign.Start)
}
}
}
}
然后使用
import {Map2} from '../components/Map2'
import AccessControl from '../utils/AccessControl'
import { geoLocationManager } from '@kit.LocationKit'
import { abilityAccessCtrl } from '@kit.AbilityKit'
import { BusinessError } from '@kit.BasicServicesKit'
@Entry
@Component
struct Test {
@Provide latitude:number = 39.9
@Provide longitude:number = 116.4
@Provide content:string = '暂时未发获取位置信息'
aboutToAppear(): void {
this.getLocation()
}
async getLocation() {
// 定位模拟器不支持 必须真机
// 1 申请位置权限
const state = await AccessControl.checkPermissions(['ohos.permission.LOCATION', 'ohos.permission.APPROXIMATELY_LOCATION'])
console.log('slj 1 申请位置权限结果:', state)
if (state) {
// 2 检查全局位置开关状态(再设置) 这玩意关了所以app都无法使用 弹框让他去开启
try {
let locationEnabled = geoLocationManager.isLocationEnabled(); // 如果是false则拉起系统弹框
console.log('slj 2 检查全局开关状态:', locationEnabled)
if (!locationEnabled) {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
const state2 = await atManager.requestGlobalSwitch(getContext(this), abilityAccessCtrl.SwitchType.LOCATION).then((data: Boolean) => {
console.info('slj 2衍伸弹窗 success/cancel:' + JSON.stringify(data));
return data
}).catch((err: BusinessError) => {
console.error('slj 2衍伸弹窗:' + JSON.stringify(err));
return false
});
if (!state2) return
}
// 3 单次获取当前设备位置 geoLocationManager.getCurrentLocation(request)
try {
geoLocationManager.getCurrentLocation({
'locatingPriority': geoLocationManager.LocatingPriority.PRIORITY_LOCATING_SPEED,
'locatingTimeoutMs': 10000
}).then((result) => { // 调用getCurrentLocation获取当前设备位置,通过promise接收上报的位置
console.log('slj 3 单次获取当前设备位置 current location: ' + JSON.stringify(result));
// : {"latitude":40.11527531549031,"longitude":116.24535796600712,"altitude":0,"accuracy":11.084914,"speed":0,"timeStamp":1735980695625,"direction":0,"timeSinceBoot":99791209379781,"additionSize":1,"additions":["requestId:d2fc6c59-d95b-4f3f-a2d0-c1c4c7a28e8f"],"additionsMap":{},"altitudeAccuracy":0,"speedAccuracy":0,"directionAccuracy":0,"uncertaintyOfTimeSinceBoot":0,"sourceType":2}
// 转化为地理位置信息
let reverseGeocodeRequest:geoLocationManager.ReverseGeoCodeRequest = {"latitude": result.latitude, "longitude": result.longitude, "maxItems": 1, locale: 'zh'};
try {
geoLocationManager.getAddressesFromLocation(reverseGeocodeRequest, (err, data) => {
if (err) {
console.log('slj 3延伸 getAddressesFromLocation err: ' + JSON.stringify(err));
} else {
console.log('slj 3延伸 getAddressesFromLocation data: ' + JSON.stringify(data));
console.log('slj ', data[0].latitude?.toString())
console.log('slj ', data[0].longitude?.toString())
console.log('slj ', data[0].placeName)
this.latitude = data[0].latitude as number
this.longitude = data[0].longitude as number
this.content = data[0].placeName as string
}
});
} catch (err) {
console.error("errCode:" + JSON.stringify(err));
}
// ==============
})
.catch((error:BusinessError) => { // 接收上报的错误码
console.error('promise, getCurrentLocation: error=' + JSON.stringify(error));
});
} catch (err) {
console.error("errCode:" + JSON.stringify(err));
}
// end
} catch (err) {
console.error("errCode:" + err.code + ", message:" + err.message);
}
}
// 细节1 持续获取 (后台运行就会自动停止 得配置长时任务)
// 细节2 把经度纬度转换为具体明细地址 中文
}
build() {
Column() {
Map2()
}
}
}