【HarmonyOS实战】 地图Marker标记:自定义图标、信息窗口与点击事件
地图上那些能点击的图标叫做Marker(标记),是地图应用最核心的 UI 元素之一。在附近加油站里,每个加油站在地图上都有一个自定义图标(加油站 SVG 图案),点击后标记会放大,底部列表也会出现。这篇文章详细讲 Marker 的添加、自定义图标配置、信息窗口显示以及点击事件处理。项目预览icon: 'station.svg', // 使用 SVG 文件作为图标icon指定图标文件名,文件放在目录
文章目录
前言
地图上那些能点击的图标叫做 Marker(标记),是地图应用最核心的 UI 元素之一。在附近加油站里,每个加油站在地图上都有一个自定义图标(加油站 SVG 图案),点击后标记会放大,底部列表也会出现。
这篇文章详细讲 Marker 的添加、自定义图标配置、信息窗口显示以及点击事件处理。
项目预览


一、addMapMaker 方法解析
// MapUtil.ets
async addMapMaker(latitude: number, longitude: number, mapController: map.MapComponentController): Promise<void> {
let markerOptions: mapCommon.MarkerOptions = {
position: {
latitude: latitude,
longitude: longitude
},
rotation: 0, // 旋转角度(0度,即不旋转)
visible: true, // 是否可见
zIndex: 0, // 层叠顺序
alpha: 1, // 透明度(1=完全不透明)
anchorU: 0.5, // 锚点X(0-1,0.5=水平居中)
anchorV: 1, // 锚点Y(0-1,1=底部对齐到坐标点)
clickable: true, // 是否可点击
flat: true, // 是否贴地(随地图倾斜)
icon: 'station.svg', // 自定义图标(SVG文件名)
collisionRule: mapCommon.CollisionRule.NAME, // 碰撞规则
};
// 添加 Marker
await mapController.addMarker(markerOptions);
}
二、MarkerOptions 每个参数详解
2.1 position:坐标位置
position: {
latitude: latitude,
longitude: longitude
}
Marker 在地图上显示的位置,使用 GCJ02 坐标(中国境内)。
2.2 anchor:锚点
anchorU: 0.5, // 水平方向锚点(0=左边, 0.5=中间, 1=右边)
anchorV: 1, // 垂直方向锚点(0=顶部, 0.5=中间, 1=底部)
锚点决定 Marker 图标的哪个点对应地图坐标:
anchorU=0.5, anchorV=1 的效果:
┌────────────┐
│ │
│ [图标] │
│ │
└─────●──────┘
↑
图标底部中央对准坐标点(像图钉一样)
对于加油站这种图标,anchorV=1(底部对齐)让图标的底部尖端精确指向坐标点,视觉上更准确。
2.3 icon:自定义图标
icon: 'station.svg', // 使用 SVG 文件作为图标
icon 指定图标文件名,文件放在 resources/base/media/ 目录下。支持 PNG、SVG 等格式。
为什么选 SVG?
| 格式 | 优点 | 缺点 |
|---|---|---|
| PNG | 支持复杂图案 | 放大会模糊,需要提供@2x/@3x多分辨率 |
| SVG | 矢量,任意放大不失真,文件小 | 不支持过于复杂的效果 |
地图标记需要在不同缩放级别下保持清晰,SVG 是更好的选择。
2.4 flat:贴地模式
flat: true,
flat: true:标记贴在地图表面,地图倾斜时标记也跟着倾斜(更立体的感觉)flat: false(默认):标记始终面向用户(始终垂直于屏幕)
2.5 zIndex:层叠顺序
zIndex: 0,
当多个 Marker 重叠时,zIndex 越大的显示在上层。默认 0,如果你希望某个重要标记始终在最上面,给它设置更大的 zIndex。
2.6 alpha:透明度
alpha: 1, // 1=完全不透明,0=完全透明,0.5=半透明
2.7 collisionRule:碰撞规则
collisionRule: mapCommon.CollisionRule.NAME,
当多个 Marker 在屏幕上位置太近时,如何处理碰撞:
| 碰撞规则 | 说明 |
|---|---|
NAME |
按名称决定显示优先级 |
NONE |
不处理碰撞(都显示) |
三、批量添加 Marker
// GasStationPage.ets - callback 里
this.stationInfoList.forEach(async (stationItem: StationData) => {
await mapUtil.addMapMaker(
stationItem.latitude,
stationItem.longitude,
this.mapController as map.MapComponentController
);
});
遍历加油站列表,为每个加油站在对应坐标添加一个 Marker。注意这里用的是 async forEach——虽然语法上没问题,但有一个潜在问题:forEach 不能正确等待 async 回调完成。
更严谨的写法:
// 更严谨:用 for...of + await 确保顺序添加
for (let stationItem of this.stationInfoList) {
await mapUtil.addMapMaker(
stationItem.latitude,
stationItem.longitude,
this.mapController as map.MapComponentController
);
}
对于这个场景,并发添加还是顺序添加差别不大,forEach 写法更简洁,但要知道它不是真正的顺序等待。
四、Marker 点击事件
在 GasStationPage 的 callback 里监听 Marker 点击:
this.mapController.on('markerClick', (marker) => {
// 1. 显示底部加油站列表
this.isShow = true;
// 2. 显示标记的信息窗口(弹出小气泡)
marker.setInfoWindowVisible(true);
// 3. 记录当前点击的标记(用于后续重置)
this.curMarker = marker;
// 4. 设置放大比例并播放动画
this.imageScale = 1.5;
mapUtil.imageAnimation(marker, this.imageScale);
// 5. 移动地图到该标记位置
mapUtil.moveToCurrentPosition(
marker.getPosition().latitude,
marker.getPosition().longitude,
mapController
);
});
marker 对象的常用方法:
| 方法 | 作用 |
|---|---|
marker.getPosition() |
获取标记的经纬度 |
marker.setInfoWindowVisible(true) |
显示信息窗口(小气泡) |
marker.setAnimation(animation) |
设置动画 |
marker.startAnimation() |
启动动画 |
marker.setVisible(true) |
设置可见性 |
marker.setIcon('newIcon.png') |
动态更换图标 |
五、信息窗口(InfoWindow)
marker.setInfoWindowVisible(true); // 显示信息窗口
marker.setInfoWindowVisible(false); // 隐藏信息窗口
信息窗口是点击 Marker 后弹出的小气泡,默认显示 Marker 的 title 和 snippet:
let markerOptions: mapCommon.MarkerOptions = {
// ...
title: '中国石化AA站', // 信息窗口标题
snippet: 'N市J区XX大街587号', // 信息窗口副标题
};
项目里没有设置 title 和 snippet,所以信息窗口是空的。调用 setInfoWindowVisible(true) 只是触发一个空白气泡,实际信息展示在底部的 bindSheet 里。
六、底部弹窗关闭时重置动画
当底部弹窗关闭时,需要把放大的 Marker 恢复原始大小:
// GasStationPage.ets - bindSheet 的 onWillDismiss 回调
.bindSheet($$this.isShow, this.bindBuilder(), {
// ...
onWillDismiss: ((dismissSheetAction: DismissSheetAction) => {
if (this.curMarker) {
this.imageScale = 1; // 恢复原始大小
mapUtil.imageAnimation(this.curMarker, this.imageScale); // 播放缩小动画
}
dismissSheetAction.dismiss(); // 确认关闭弹窗
})
});
七、完整的 Marker 生命周期
页面进入 → init()
└─> addMapMaker() × 4 (添加4个加油站标记)
用户点击标记:
└─> on('markerClick')
├─> setInfoWindowVisible(true) (显示气泡)
├─> imageAnimation(scale=1.5) (放大动画)
├─> moveToCurrentPosition() (移动镜头)
└─> isShow = true (显示底部列表)
用户关闭底部列表:
└─> onWillDismiss
└─> imageAnimation(scale=1) (缩小恢复)
总结
Marker 是地图的核心 UI 元素:
- 创建:构建
MarkerOptions(坐标、图标、锚点、可见性等) - 添加:
mapController.addMarker(markerOptions),注意是 async 的 - 监听点击:
mapController.on('markerClick', callback),callback 接收 marker 对象 - 动态操作:通过 marker 对象的方法控制显示/隐藏/动画
自定义图标使用 SVG,锚点 anchorV=1 让图标底部对准坐标点,体验更准确。
下一篇讲相机动画——地图镜头移动是怎么实现流畅动画效果的。
更多推荐



所有评论(0)