鸿蒙 Map Kit 实战:调用华为地图服务,开发一个“周边美食搜索”原子化服务卡片
传统的找店流程:解锁手机 -> 找 App -> 点击启动 -> 等广告 -> 搜索“美食” -> 筛选附近。点亮屏幕 -> 看一眼桌面卡片。定位:获取用户当前经纬度。搜索 (Site Kit):根据坐标搜索周边 POI (兴趣点)。展示 (Form):将数据渲染到轻量级的卡片上。导航 (Map Kit):点击卡片,拉起地图进行导航。fill:#333;important;important;fi
标签: #HarmonyOS #MapKit #SiteKit #元服务 #ArkTS #LBS
🍱 前言:当 LBS 遇上万能卡片
传统的找店流程:解锁手机 -> 找 App -> 点击启动 -> 等广告 -> 搜索“美食” -> 筛选附近。
鸿蒙元服务流程:点亮屏幕 -> 看一眼桌面卡片。
这背后的技术链路其实并不复杂:
- 定位:获取用户当前经纬度。
- 搜索 (Site Kit):根据坐标搜索周边 POI (兴趣点)。
- 展示 (Form):将数据渲染到轻量级的卡片上。
- 导航 (Map Kit):点击卡片,拉起地图进行导航。
数据流转图 (Mermaid):
🛠️ 一、 准备工作:AGC 配置
在使用华为地图服务前,必须在 AppGallery Connect (AGC) 上开通权限。
- 注册开发者账号:登录华为开发者联盟。
- 创建项目与应用:获取
client_id。 - 开启 API:在“我的项目” -> “API管理”中,开启 Map Kit 和 Site Kit。
- 配置工程:在
module.json5中配置client_id。
"module": {
"metadata": [
{
"name": "client_id",
"value": "你的_CLIENT_ID" // ⚠️ 别忘了这个!
}
]
}
📡 二、 核心逻辑:获取定位与周边搜索
我们需要在 EntryFormAbility (卡片的生命周期管理类) 或者后台 Service 中处理逻辑。为了演示清晰,我们直接看核心的 API 调用。
首先,申请权限 ohos.permission.LOCATION 和 ohos.permission.APPROXIMATELY_LOCATION。
1. 引入模块
import { site } from '@kit.MapKit'; // 鸿蒙 Next 中 Site Kit 已集成在 Map Kit 命名空间下
import { geoLocationManager } from '@kit.LocationKit';
2. 封装搜索函数
我们需要调用 Site Kit 的 nearbySearch 接口。
async function searchNearbyFood(lat: number, lng: number) {
// 1. 构建搜索请求
let searchRequest: site.NearbySearchRequest = {
location: {
latitude: lat,
longitude: lng
},
query: "美食", // 搜索关键词
radius: 1000, // 搜索半径 1公里
pageSize: 4, // 卡片位置有限,只拿前4个
pageIndex: 1,
poiType: [site.LocationType.RESTAURANT] // 指定类型为餐厅
};
try {
// 2. 调用华为 Site Kit
let result = await site.nearbySearch(searchRequest);
if (result.sites) {
// 3. 转换数据格式,用于卡片渲染
let foodList = result.sites.map(item => {
return {
name: item.name,
address: item.formatAddress,
distance: item.distance,
// 如果有图片 url,也可以放这里
};
});
return foodList;
}
} catch (err) {
console.error("Search failed: " + JSON.stringify(err));
}
return [];
}
🖼️ 三、 界面实现:ArkTS 卡片 UI
卡片 UI (Widget) 和普通 Page 的写法略有不同,它更轻量。
FoodCard.ets:
@Entry
@Component
struct FoodCard {
// 接收 FormExtensionAbility 传来的数据
@LocalStorageProp('foodList') foodList: Array<any> = [];
build() {
Column() {
Text('附近美食推荐')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.padding({ left: 12, top: 10, bottom: 5 })
.width('100%')
if (this.foodList.length === 0) {
Text('正在定位中...').fontSize(12).margin(10)
} else {
// 使用 Grid 布局展示 4 个餐厅
Grid() {
ForEach(this.foodList, (item: any) => {
GridItem() {
Column() {
// 模拟餐厅图标
Image($r('app.media.icon_food'))
.width(30)
.height(30)
.borderRadius(8)
Text(item.name)
.fontSize(10)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 4 })
Text(`${item.distance}m`)
.fontSize(9)
.fontColor(Color.Gray)
}
// 点击事件:拉起主应用,并传递参数
.onClick(() => {
postCardAction(this, {
action: 'router',
abilityName: 'EntryAbility',
params: {
targetPage: 'MapPage',
poiName: item.name,
lat: item.location.latitude,
lng: item.location.longitude
}
});
})
.backgroundColor('#F5F5F5')
.borderRadius(8)
.padding(8)
.width('100%')
.height(80)
}
})
}
.columnsTemplate('1fr 1fr 1fr 1fr') // 4列
.columnsGap(8)
.padding(12)
}
}
.width('100%')
.height('100%')
.backgroundColor(Color.White)
}
}
🗺️ 四、 落地页:Map Kit 导航展示
当用户点击卡片上的餐厅时,跳转到 App 内的 MapPage。这里我们需要使用 Map Component 展示具体位置。
MapPage.ets:
import { MapComponent, mapCommon, map } from '@kit.MapKit';
@Entry
@Component
struct MapPage {
@State mapController: map.MapComponentController = new map.MapComponentController();
// 从路由参数中获取的目标坐标
targetLat: number = 0;
targetLng: number = 0;
targetName: string = "";
aboutToAppear() {
// 获取路由参数逻辑 (略)
}
build() {
Stack() {
// 地图组件
MapComponent({
mapOptions: {
position: {
target: {
latitude: this.targetLat,
longitude: this.targetLng
},
zoom: 15 // 缩放级别
}
},
mapCallback: (err, controller) => {
if (!err) {
this.mapController = controller;
// 地图加载完成后,添加一个标记
this.addMarker();
}
}
})
.width('100%')
.height('100%')
}
}
addMarker() {
let markerOptions: mapCommon.MarkerOptions = {
position: {
latitude: this.targetLat,
longitude: this.targetLng
},
title: this.targetName,
clickable: true
};
this.mapController.addMarker(markerOptions);
}
}
⚠️ 五、 避坑指南
- Client ID 报错:如果你发现地图白屏或者搜索报错 6004/010001,99% 是因为
module.json5里的client_id没配对,或者指纹证书(SHA256)没在 AGC 后台录入。 - 定位延迟:在卡片上直接做实时定位(GPS)非常耗电且慢。最佳实践是:App 主程序启动时缓存最后一次位置,或者在 FormAbility 的
onUpdateForm中使用“上次已知位置”快速刷新,再异步发起精确定位更新。 - 模拟器问题:DevEco Studio 的模拟器默认定位可能在海上。记得在模拟器的设置里手动 Mock 一个经纬度(比如深圳坂田)。
🎯 总结
通过 Site Kit 强大的数据能力和 Map Kit 的渲染能力,配合鸿蒙的 元服务卡片,我们用很低的代码成本,就实现了一个原生级的“周边美食猎手”。
这种“服务找人”的体验,正是 HarmonyOS Next 极力推崇的开发范式。
Next Step:
现在的卡片是静态刷新的。尝试引入 ArkTS 卡片动画,当数据加载出来时,给 GridItem 加一个渐入效果,让卡片看起来更灵动!
更多推荐




所有评论(0)