第5篇:地图首页与核心交互
教程目标
通过本篇教程,你将学会:
- 集成高德地图SDK
- 配置地图API Key
- 实现地图首页布局
- 实现地图基础交互(缩放、平移、定位)
- 实现定位服务
- 设计地图工具栏
完成本教程后,你将拥有一个以地图为核心的应用首页。
一、高德地图SDK集成
1.1 添加SDK依赖
打开 oh-package.json5,在 dependencies 中添加高德地图相关依赖:
json5
{
"dependencies": {
"@amap/amap_lbs_common": "^2.2.5",
"@amap/amap_lbs_map3d": "^2.2.5",
"@amap/amap_lbs_search": "^1.0.2",
"@amap/amap_lbs_navi": "^1.0.0"
}
}
1.2 安装依赖
在终端中执行:
bash
ohpm install
等待依赖安装完成。
1.3 申请高德地图API Key
- 访问高德开放平台:https://lbs.amap.com/
- 注册并登录账号
- 进入"应用管理" → "我的应用"
- 点击"创建新应用"
- 填写应用信息
- 添加Key,选择"HarmonyOS"平台
- 填写应用包名:
com.leson.zhongdi - 获取API Key(格式如:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx)
重要提示:请妥善保管API Key,不要泄露。
二、配置地图常量
2.1 创建 MapConstants.ets
在 ets/constants/ 目录下创建 MapConstants.ets:
typescript
/**
* 地图相关常量
*/
export class MapConstants {
// 高德地图API Key(请替换为你自己的Key)
static readonly AMAP_API_KEY = 'your_amap_api_key_here';
// 默认地图中心点(北京天安门)
static readonly DEFAULT_CENTER_LATITUDE = 39.9042;
static readonly DEFAULT_CENTER_LONGITUDE = 116.4074;
// 默认缩放级别
static readonly DEFAULT_ZOOM = 12;
// 地图类型
static readonly MAP_TYPE_NORMAL = 1; // 标准地图
static readonly MAP_TYPE_SATELLITE = 2; // 卫星地图
// 定位相关
static readonly LOCATION_TIMEOUT = 10000; // 定位超时时间(毫秒)
static readonly LOCATION_INTERVAL = 5000; // 定位间隔(毫秒)
}
注意 :将 your_amap_api_key_here 替换为你申请的API Key。
三、创建地图数据模型
3.1 创建 MapModels.ets
在 ets/models/ 目录下创建 MapModels.ets:
typescript
/**
* 地图相关数据模型
*/
/**
* 地图位置
*/
export interface MapLocation {
latitude: number; // 纬度
longitude: number; // 经度
}
/**
* 地图配置
*/
export interface MapViewConfig {
center: MapLocation;
zoom: number;
mapType: number;
}
/**
* 地块地图标记
*/
export interface FieldMapMarker {
id: string;
name: string;
location: MapLocation;
area: number;
status: FieldMarkerStatus;
color: string;
description?: string;
}
/**
* 地块标记状态
*/
export enum FieldMarkerStatus {
PLANTING = 'planting', // 种植中
ATTENTION = 'attention', // 需关注
URGENT = 'urgent', // 紧急
IDLE = 'idle' // 闲置
}
/**
* 地图统计信息
*/
export interface MapStatistics {
totalFields: number;
plantingFields: number;
idleFields: number;
totalArea: number;
}
四、初始化地图SDK
4.1 在 EntryAbility 中初始化
打开 ets/entryability/EntryAbility.ets,添加地图SDK初始化:
typescript
import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
import { hilog } from '@kit.PerformanceAnalysisKit';
import { window } from '@kit.ArkUI';
import { StorageUtil } from '../utils/StorageUtil';
import { ThemeManager } from '../utils/ThemeManager';
import { MapsInitializer } from '@amap/amap_lbs_map3d';
import { MapConstants } from '../constants/MapConstants';
export default class EntryAbility extends UIAbility {
onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onCreate');
}
onDestroy(): void {
hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onDestroy');
}
async onWindowStageCreate(windowStage: window.WindowStage): Promise<void> {
hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onWindowStageCreate');
// 初始化高德地图SDK
try {
MapsInitializer.setApiKey(MapConstants.AMAP_API_KEY);
hilog.info(0x0000, 'PlantingAssistant', 'AMap SDK initialized with key');
} catch (error) {
hilog.error(0x0000, 'PlantingAssistant', 'Failed to initialize AMap SDK: %{public}s', JSON.stringify(error));
}
// 初始化存储
try {
await StorageUtil.init(this.context);
hilog.info(0x0000, 'PlantingAssistant', 'StorageUtil initialized');
} catch (error) {
hilog.error(0x0000, 'PlantingAssistant', 'Failed to initialize StorageUtil: %{public}s', JSON.stringify(error));
}
// 初始化主题管理器
try {
await ThemeManager.getInstance().init();
hilog.info(0x0000, 'PlantingAssistant', 'ThemeManager initialized');
} catch (error) {
hilog.error(0x0000, 'PlantingAssistant', 'Failed to initialize ThemeManager: %{public}s', JSON.stringify(error));
}
// 加载主页面
windowStage.loadContent('pages/WelcomePage', (err) => {
if (err.code) {
hilog.error(0x0000, 'PlantingAssistant', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err));
return;
}
hilog.info(0x0000, 'PlantingAssistant', 'Succeeded in loading the content');
});
}
onWindowStageDestroy(): void {
hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onWindowStageDestroy');
}
onForeground(): void {
hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onForeground');
}
onBackground(): void {
hilog.info(0x0000, 'PlantingAssistant', '%{public}s', 'Ability onBackground');
}
}
关键点:
- 使用
MapsInitializer.setApiKey()设置API Key - 在应用启动时初始化,确保地图功能可用
- 添加错误处理,记录初始化日志
五、创建地图服务
5.1 创建 AMapLocationService.ets
在 ets/services/ 目录下创建 AMapLocationService.ets:
typescript
/**
* 高德定位服务
* 提供GPS定位和逆地理编码功能
*/
import { common } from '@kit.AbilityKit';
import { abilityAccessCtrl, Permissions, PermissionRequestResult } from '@kit.AbilityKit';
import { geoLocationManager } from '@kit.LocationKit';
/**
* 定位结果接口
*/
export interface LocationResult {
latitude: number;
longitude: number;
address?: string;
accuracy?: number;
timestamp?: number;
}
/**
* 定位回调接口
*/
export interface LocationCallback {
onSuccess: (location: LocationResult) => void;
onError: (error: string) => void;
}
export class AMapLocationService {
private static instance: AMapLocationService;
private context: common.UIAbilityContext | null = null;
private hasLocationPermission: boolean = false;
private constructor() {}
static getInstance(): AMapLocationService {
if (!AMapLocationService.instance) {
AMapLocationService.instance = new AMapLocationService();
}
return AMapLocationService.instance;
}
/**
* 初始化服务
*/
initialize(context: common.UIAbilityContext): void {
this.context = context;
console.info('[AMapLocationService] Service initialized');
}
/**
* 请求定位权限
*/
async requestLocationPermission(): Promise<boolean> {
if (!this.context) {
console.error('[AMapLocationService] Context not initialized');
return false;
}
try {
const permissions: Array<Permissions> = [
'ohos.permission.APPROXIMATELY_LOCATION',
'ohos.permission.LOCATION'
];
const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
const data: PermissionRequestResult = await atManager.requestPermissionsFromUser(
this.context,
permissions
);
const grantStatus: Array<number> = data.authResults;
let allGranted = true;
for (let i = 0; i < grantStatus.length; i++) {
if (grantStatus[i] !== 0) {
allGranted = false;
console.warn('[AMapLocationService] Permission denied:', permissions[i]);
}
}
this.hasLocationPermission = allGranted;
console.info('[AMapLocationService] Location permission:', allGranted ? 'granted' : 'denied');
return allGranted;
} catch (error) {
console.error('[AMapLocationService] Request permission failed:', error);
return false;
}
}
/**
* 获取当前位置
*/
async getCurrentLocation(callback: LocationCallback): Promise<void> {
if (!this.hasLocationPermission) {
const granted = await this.requestLocationPermission();
if (!granted) {
callback.onError('定位权限未授予');
return;
}
}
try {
const requestInfo: geoLocationManager.CurrentLocationRequest = {
priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,
scenario: geoLocationManager.LocationRequestScenario.UNSET,
maxAccuracy: 0
};
geoLocationManager.getCurrentLocation(requestInfo, (err, location) => {
if (err) {
console.error('[AMapLocationService] Get location failed:', JSON.stringify(err));
callback.onError('定位失败');
return;
}
const result: LocationResult = {
latitude: location.latitude,
longitude: location.longitude,
accuracy: location.accuracy,
timestamp: Date.now()
};
console.info('[AMapLocationService] Location obtained:', JSON.stringify(result));
callback.onSuccess(result);
});
} catch (error) {
console.error('[AMapLocationService] Get location error:', error);
callback.onError('定位异常');
}
}
}
六、创建地图首页
6.1 创建 Map 目录
在 ets/pages/ 下创建 Map 目录。
6.2 创建 FieldMapPage.ets
在 Map/ 目录下创建 FieldMapPage.ets:
typescript
import { Map, MapOptions } from '@amap/amap_lbs_map3d';
import { AMapLocationService, LocationResult } from '../../services/AMapLocationService';
import { MapConstants } from '../../constants/MapConstants';
import { common } from '@kit.AbilityKit';
@Entry
@ComponentV2
struct FieldMapPage {
@Local isLoading: boolean = true;
@Local currentLocation: LocationResult | null = null;
@Local mapReady: boolean = false;
private locationService = AMapLocationService.getInstance();
private context: common.UIAbilityContext | null = null;
aboutToAppear(): void {
this.context = getContext(this) as common.UIAbilityContext;
this.locationService.initialize(this.context);
this.requestLocation();
}
build() {
Stack() {
// 地图容器
Column() {
Map({
mapOptions: this.getMapOptions(),
onMapReady: () => {
this.onMapReady();
}
})
.width('100%')
.height('100%')
}
// 工具栏
Column() {
this.buildToolbar()
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.End)
.padding(16)
// 加载提示
if (this.isLoading) {
Column() {
LoadingProgress()
.width(40)
.height(40)
.color($r('app.color.primary_professional'))
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
.backgroundColor('#80000000')
}
}
.width('100%')
.height('100%')
}
/**
* 获取地图配置
*/
private getMapOptions(): MapOptions {
return {
center: {
latitude: MapConstants.DEFAULT_CENTER_LATITUDE,
longitude: MapConstants.DEFAULT_CENTER_LONGITUDE
},
zoom: MapConstants.DEFAULT_ZOOM,
mapType: MapConstants.MAP_TYPE_NORMAL
};
}
/**
* 地图准备完成回调
*/
private onMapReady(): void {
console.info('[FieldMapPage] Map ready');
this.mapReady = true;
this.isLoading = false;
}
/**
* 请求定位
*/
private async requestLocation(): Promise<void> {
await this.locationService.getCurrentLocation({
onSuccess: (location: LocationResult) => {
console.info('[FieldMapPage] Location success:', JSON.stringify(location));
this.currentLocation = location;
this.isLoading = false;
},
onError: (error: string) => {
console.error('[FieldMapPage] Location error:', error);
this.isLoading = false;
}
});
}
/**
* 构建工具栏
*/
@Builder
buildToolbar() {
Column({ space: 12 }) {
// 定位按钮
Button() {
Text('📍')
.fontSize(24)
}
.width(48)
.height(48)
.backgroundColor($r('app.color.card_background'))
.borderRadius(24)
.onClick(() => {
this.onLocationClick();
})
// 地图类型切换
Button() {
Text('🗺️')
.fontSize(24)
}
.width(48)
.height(48)
.backgroundColor($r('app.color.card_background'))
.borderRadius(24)
.onClick(() => {
this.onMapTypeClick();
})
// 添加地块
Button() {
Text('➕')
.fontSize(24)
}
.width(48)
.height(48)
.backgroundColor($r('app.color.primary_professional'))
.borderRadius(24)
.onClick(() => {
this.onAddFieldClick();
})
}
.alignItems(HorizontalAlign.End)
}
private onLocationClick(): void {
console.info('[FieldMapPage] Location button clicked');
this.requestLocation();
}
private onMapTypeClick(): void {
console.info('[FieldMapPage] Map type button clicked');
}
private onAddFieldClick(): void {
console.info('[FieldMapPage] Add field button clicked');
}
}
6.3 配置页面路由
打开 main_pages.json,添加:
json
{
"src": [
"pages/WelcomePage",
"pages/Index",
"pages/Map/FieldMapPage",
"pages/OnboardingFlow/ModeSelectionPage",
"pages/OnboardingFlow/LocationPage",
"pages/OnboardingFlow/GoalsPage"
]
}
七、运行与测试
7.1 测试步骤
- 清除应用数据
- 运行应用
- 完成引导流程
- 进入地图页面
- 授予定位权限
- 观察地图加载
预期效果:
- ✅ 地图正常显示
- ✅ 定位权限请求正常
- ✅ 工具栏按钮显示
- ✅ 点击按钮有响应
八、总结
本篇教程完成了:
- ✅ 高德地图SDK集成
- ✅ 定位服务实现
- ✅ 地图首页布局
- ✅ 基础交互功能
九、下一步
下一篇将学习地块管理系统的数据模型与列表实现。
教程版本 :v1.0
更新日期 :2026-01
适用版本:DevEco Studio 5.0+, HarmonyOS API 17+