[鸿蒙2025领航者闯关]HarmonyOS中开发高德地图第五篇:定位蓝点效果
2026-01-03 09:49 tlnshuju 阅读(0) 评论(0) 收藏 举报第五篇:定位蓝点功能
本篇教程将学习如何在地图上显示用户当前位置(定位蓝点),包括权限申请、定位样式自定义等。
学习目标
- 申请定位权限
- 显示定位蓝点
- 自定义定位蓝点样式
- 理解不同的定位模式
1. 定位权限配置
1.1 module.json5 权限声明
{"module": {"requestPermissions": [{"name": "ohos.permission.LOCATION","reason": "$string:permission_location_reason","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}},{"name": "ohos.permission.APPROXIMATELY_LOCATION","reason": "$string:permission_location_reason","usedScene": {"abilities": ["EntryAbility"],"when": "inuse"}}]}
}
1.2 权限说明
| 权限 | 说明 |
|---|---|
ohos.permission.LOCATION | 精确定位权限 |
ohos.permission.APPROXIMATELY_LOCATION | 模糊定位权限 |
注意: 申请精确定位时必须同时申请模糊定位权限。
2. 定位模式说明
MyLocationStyle 提供了多种定位模式:
| 模式 | 常量 | 说明 |
|---|---|---|
| 只展示 | LOCATION_TYPE_SHOW | 只显示位置,不移动地图 |
| 定位一次 | LOCATION_TYPE_LOCATE | 定位一次并移动到该位置 |
| 跟随 | LOCATION_TYPE_FOLLOW | 持续跟随位置移动 |
| 地图旋转 | LOCATION_TYPE_MAP_ROTATE | 地图随手机方向旋转 |
| 定位点旋转 | LOCATION_TYPE_LOCATION_ROTATE | 定位点随方向旋转 |
| 跟随不移中心 | LOCATION_TYPE_FOLLOW_NO_CENTER | 跟随但不移动地图中心 |
| 地图旋转不移中心 | LOCATION_TYPE_MAP_ROTATE_NO_CENTER | 地图旋转但不移中心 |
| 定位点旋转不移中心 | LOCATION_TYPE_LOCATION_ROTATE_NO_CENTER | 定位点旋转但不移中心 |
3. 完整代码示例
创建文件 entry/src/main/ets/pages/Demo04_Location.ets:
import {
AMap,
MapView,
MapViewComponent,
MapViewManager,
MapViewCreateCallback,
CameraUpdateFactory,
LatLng,
MyLocationStyle,
BitmapDescriptorFactory,
OnLocationChangedListener
} from '@amap/amap_lbs_map3d';
import { abilityAccessCtrl, bundleManager, common, Permissions } from '@kit.AbilityKit';
import { BusinessError } from '@kit.BasicServicesKit';
import { geoLocationManager } from '@kit.LocationKit';
const MAP_VIEW_NAME = 'LocationDemo';
// 获取应用上下文
let globalContext: Context;
/**
* 定位模式选项
*/
interface LocationModeOption {
name: string;
mode: number;
}
@Entry
@Component
struct Demo04_Location {
private mapView: MapView | undefined = undefined;
private aMap: AMap | undefined = undefined;
private locationStyle: MyLocationStyle = new MyLocationStyle();
private locationIntervalId: number = 0;
@State isMapReady: boolean = false;
@State hasLocationPermission: boolean = false;
@State isLocationEnabled: boolean = false;
@State currentMode: string = '只展示';
@State locationInfo: string = '等待定位...';
@State permissionStatus: string = '检查权限中...';
// 定位模式选项
private locationModes: LocationModeOption[] = [
{ name: '只展示', mode: MyLocationStyle.LOCATION_TYPE_SHOW },
{ name: '定位一次', mode: MyLocationStyle.LOCATION_TYPE_LOCATE },
{ name: '跟随', mode: MyLocationStyle.LOCATION_TYPE_FOLLOW },
{ name: '地图旋转', mode: MyLocationStyle.LOCATION_TYPE_MAP_ROTATE },
{ name: '定位点旋转', mode: MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE }
];
private mapViewCreateCallback: MapViewCreateCallback =
(mapview: MapView | undefined, mapViewName: string | undefined) => {
if (!mapview || mapViewName !== MAP_VIEW_NAME) return;
this.mapView = mapview;
this.mapView.onCreate();
this.mapView.getMapAsync(async (map: AMap) => {
this.aMap = map;
this.isMapReady = true;
// 启用缩放控件
map.getUiSettings()?.setZoomControlsEnabled(true);
// 检查权限并配置定位
await this.checkAndRequestPermission();
});
};
/**
* 检查并请求定位权限
*/
private async checkAndRequestPermission(): Promise<void> {try {const permission: Permissions = 'ohos.permission.LOCATION';const grantStatus = await this.checkAccessToken(permission);if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED) {this.hasLocationPermission = true;this.permissionStatus = '✅ 定位权限已授予';// 配置定位await this.configureLocation();} else {this.permissionStatus = '⏳ 正在请求权限...';// 请求权限await this.requestPermissions();}} catch (error) {console.error('[Location] Permission check failed:', error);this.permissionStatus = '❌ 权限检查失败';}}/*** 检查权限状态*/private async checkAccessToken(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {const atManager = abilityAccessCtrl.createAtManager();let grantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;try {const bundleInfo = await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);const tokenId = bundleInfo.appInfo.accessTokenId;grantStatus = await atManager.checkAccessToken(tokenId, permission);} catch (error) {console.error('[Location] Check token failed:', error);}return grantStatus;}/*** 请求权限*/private async requestPermissions(): Promise<void> {const permissions: Permissions[] = ['ohos.permission.APPROXIMATELY_LOCATION','ohos.permission.LOCATION'];try {const context = getContext(this) as common.UIAbilityContext;const atManager = abilityAccessCtrl.createAtManager();const result = await atManager.requestPermissionsFromUser(context, permissions);const allGranted = result.authResults.every(status => status === 0);if (allGranted) {this.hasLocationPermission = true;this.permissionStatus = '✅ 定位权限已授予';await this.configureLocation();} else {this.hasLocationPermission = false;this.permissionStatus = '❌ 用户拒绝了定位权限';}} catch (error) {console.error('[Location] Request permission failed:', error);this.permissionStatus = '❌ 权限请求失败';}}/*** 配置定位蓝点*/private async configureLocation(): Promise<void> {if (!this.aMap) return;try {// 设置定位样式this.locationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_SHOW);// 自定义定位图标(可选)try {const icon = await BitmapDescriptorFactory.fromRawfilePath(globalContext,'location_icon.png');if (icon) {this.locationStyle.myLocationIcon(icon);}} catch (e) {console.info('[Location] Using default location icon');}// 设置精度圈样式this.locationStyle.strokeColor(0x800000FF); // 边框颜色(带透明度的蓝色)this.locationStyle.strokeWidth(2); // 边框宽度this.locationStyle.radiusFillColor(0x200000FF); // 填充颜色// 应用样式this.aMap.setMyLocationStyle(this.locationStyle);// 设置定位数据源this.aMap.setLocationSource(this);// 启用定位图层this.aMap.setMyLocationEnabled(true);this.isLocationEnabled = true;this.locationInfo = '定位已启用,等待位置更新...';console.info('[Location] Location configured successfully');} catch (error) {console.error('[Location] Configure location failed:', error);this.locationInfo = '定位配置失败';}}/*** LocationSource接口实现 - 激活定位*/activate(listener: OnLocationChangedListener): void {console.info('[Location] activate called');// 使用系统定位获取位置this.startSystemLocation(listener);}/*** LocationSource接口实现 - 停用定位*/deactivate(): void {console.info('[Location] deactivate called');if (this.locationIntervalId > 0) {clearInterval(this.locationIntervalId);this.locationIntervalId = 0;}}/*** 启动系统定位*/private startSystemLocation(listener: OnLocationChangedListener): void {// 定位请求配置const requestInfo: geoLocationManager.CurrentLocationRequest = {priority: geoLocationManager.LocationRequestPriority.FIRST_FIX,scenario: geoLocationManager.LocationRequestScenario.UNSET,maxAccuracy: 100};// 获取当前位置geoLocationManager.getCurrentLocation(requestInfo).then((location: geoLocationManager.Location) => {console.info('[Location] Got location:', location.latitude, location.longitude);// 转换为高德定位格式const amapLocation: geoLocationManager.Location = {latitude: location.latitude,longitude: location.longitude,altitude: location.altitude,accuracy: location.accuracy,speed: location.speed,direction: location.direction,timeStamp: location.timeStamp,timeSinceBoot: location.timeSinceBoot,altitudeAccuracy: location.altitudeAccuracy || 0,speedAccuracy: location.speedAccuracy || 0,directionAccuracy: location.directionAccuracy || 0,uncertaintyOfTimeSinceBoot: location.uncertaintyOfTimeSinceBoot || 0,sourceType: location.sourceType || 1};// 更新位置到地图listener.onLocationChanged(amapLocation);// 更新UI显示this.locationInfo = `经度: ${location.longitude.toFixed(6)}\n纬度: ${location.latitude.toFixed(6)}\n精度: ${location.accuracy?.toFixed(0) || '未知'}米`;// 移动地图到当前位置if (this.aMap) {const latLng = new LatLng(location.latitude, location.longitude);this.aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16));}}).catch((error: BusinessError) => {console.error('[Location] Get location failed:', error.message);this.locationInfo = '获取位置失败: ' + error.message;// 使用模拟位置(开发测试用)this.useSimulatedLocation(listener);});// 设置定时更新(可选)if (this.locationIntervalId === 0) {this.locationIntervalId = setInterval(() => {geoLocationManager.getCurrentLocation(requestInfo).then((location) => {const amapLocation: geoLocationManager.Location = {latitude: location.latitude,longitude: location.longitude,altitude: location.altitude,accuracy: location.accuracy,speed: location.speed,direction: location.direction,timeStamp: location.timeStamp,timeSinceBoot: location.timeSinceBoot,altitudeAccuracy: location.altitudeAccuracy || 0,speedAccuracy: location.speedAccuracy || 0,directionAccuracy: location.directionAccuracy || 0,uncertaintyOfTimeSinceBoot: location.uncertaintyOfTimeSinceBoot || 0,sourceType: location.sourceType || 1};listener.onLocationChanged(amapLocation);this.locationInfo = `经度: ${location.longitude.toFixed(6)}\n纬度: ${location.latitude.toFixed(6)}\n精度: ${location.accuracy?.toFixed(0) || '未知'}米`;}).catch(() => {});}, 5000);}}/*** 使用模拟位置(开发测试用)*/private useSimulatedLocation(listener: OnLocationChangedListener): void {// 模拟北京位置const simulatedLocation: geoLocationManager.Location = {latitude: 39.909187,longitude: 116.397451,altitude: 50,accuracy: 100,speed: 0,direction: 0,timeStamp: Date.now(),timeSinceBoot: 0,altitudeAccuracy: 0,speedAccuracy: 0,directionAccuracy: 0,uncertaintyOfTimeSinceBoot: 0,sourceType: 1};listener.onLocationChanged(simulatedLocation);this.locationInfo = `[模拟] 经度: ${simulatedLocation.longitude.toFixed(6)}\n纬度: ${simulatedLocation.latitude.toFixed(6)}`;if (this.aMap) {const latLng = new LatLng(simulatedLocation.latitude, simulatedLocation.longitude);this.aMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16));}}/*** 切换定位模式*/private setLocationMode(option: LocationModeOption): void {if (!this.aMap) return;this.locationStyle.myLocationType(option.mode);this.aMap.setMyLocationStyle(this.locationStyle);this.currentMode = option.name;console.info('[Location] Mode changed to:', option.name);}/*** 切换定位开关*/private toggleLocation(): void {if (!this.aMap) return;this.isLocationEnabled = !this.isLocationEnabled;this.aMap.setMyLocationEnabled(this.isLocationEnabled);if (!this.isLocationEnabled) {this.locationInfo = '定位已关闭';}}aboutToAppear(): void {globalContext = getContext(this).getApplicationContext();MapViewManager.getInstance().registerMapViewCreatedCallback(this.mapViewCreateCallback);}aboutToDisappear(): void {// 停止定位更新if (this.locationIntervalId > 0) {clearInterval(this.locationIntervalId);this.locationIntervalId = 0;}// 关闭定位if (this.aMap) {this.aMap.setMyLocationEnabled(false);}MapViewManager.getInstance().unregisterMapViewCreatedCallback(this.mapViewCreateCallback);if (this.mapView) {this.mapView.onDestroy();this.mapView = undefined;this.aMap = undefined;}}build() {Column() {// 标题栏Row() {Text('定位蓝点功能').fontSize(18).fontWeight(FontWeight.Bold).fontColor(Color.White)}.width('100%').height(50).padding({ left: 16 }).backgroundColor('#03A9F4')// 地图区域Stack() {MapViewComponent({ mapViewName: MAP_VIEW_NAME }).width('100%').height('100%')// 状态信息面板Column() {Text('定位状态').fontSize(12).fontWeight(FontWeight.Bold).fontColor('#333')Text(this.permissionStatus).fontSize(11).fontColor('#666').margin({ top: 4 })Text(this.locationInfo).fontSize(11).fontColor('#666').margin({ top: 4 })Text(`当前模式: ${this.currentMode}`).fontSize(11).fontColor('#2196F3').margin({ top: 4 })}.padding(10).backgroundColor('rgba(255,255,255,0.95)').borderRadius(8).position({ x: 10, y: 10 }).alignItems(HorizontalAlign.Start)}.width('100%').layoutWeight(1)// 控制面板Column() {// 定位开关Row() {Text('定位蓝点').fontSize(14).fontColor('#333')Blank()Toggle({ type: ToggleType.Switch, isOn: this.isLocationEnabled }).enabled(this.hasLocationPermission).onChange((isOn: boolean) => {this.isLocationEnabled = isOn;this.aMap?.setMyLocationEnabled(isOn);})}.width('100%').height(44).padding({ left: 12, right: 12 }).backgroundColor(Color.White).borderRadius(8).margin({ bottom: 8 })// 定位模式选择Text('定位模式').fontSize(14).fontWeight(FontWeight.Bold).width('100%').margin({ bottom: 8 })Flex({ wrap: FlexWrap.Wrap }) {ForEach(this.locationModes, (option: LocationModeOption) => {Button(option.name).fontSize(11).height(32).backgroundColor(this.currentMode === option.name ? '#03A9F4' : '#e0e0e0').fontColor(this.currentMode === option.name ? Color.White : '#333').margin({ right: 6, bottom: 6 }).enabled(this.hasLocationPermission && this.isLocationEnabled).onClick(() => this.setLocationMode(option))})}// 重新定位按钮Button('重新定位').width('100%').height(40).margin({ top: 8 }).enabled(this.hasLocationPermission).onClick(() => this.configureLocation())}.padding(12).backgroundColor('#f5f5f5')}.width('100%').height('100%')}}
4. MyLocationStyle 完整API
class MyLocationStyle {
// 设置定位模式
myLocationType(type: number): MyLocationStyle;
// 设置定位图标
myLocationIcon(icon: BitmapDescriptor): MyLocationStyle;
// 设置定位间隔(毫秒)
interval(interval: number): MyLocationStyle;
// 精度圈边框颜色
strokeColor(color: number): MyLocationStyle;
// 精度圈边框宽度
strokeWidth(width: number): MyLocationStyle;
// 精度圈填充颜色
radiusFillColor(color: number): MyLocationStyle;
// 是否显示精度圈
showMyLocation(show: boolean): MyLocationStyle;
}
5. 颜色值说明
高德地图使用ARGB格式的颜色值:
// 格式: 0xAARRGGBB
// AA: 透明度 (00=完全透明, FF=完全不透明)
// RR: 红色
// GG: 绿色
// BB: 蓝色
// 示例
0x800000FF // 半透明蓝色
0x200000FF // 较透明的蓝色
0xFF00FF00 // 不透明绿色
6. 实用技巧
6.1 判断定位服务是否可用
import { geoLocationManager } from '@kit.LocationKit';
const isEnabled = geoLocationManager.isLocationEnabled();
if (!isEnabled) {
// 提示用户开启定位服务
}
6.2 引导用户开启权限
import { common } from '@kit.AbilityKit';
// 跳转到应用设置页面
const context = getContext(this) as common.UIAbilityContext;
const want: common.Want = {
bundleName: 'com.huawei.hmos.settings',
abilityName: 'com.huawei.hmos.settings.MainAbility',
uri: 'application_info_entry',
parameters: {
pushParams: context.abilityInfo.bundleName
}
};
context.startAbility(want);
本篇小结
本篇教程我们学习了:
- ✅ 定位权限的申请和检查
- ✅ 定位蓝点的显示与配置
- ✅ 多种定位模式的切换
- ✅ 自定义定位蓝点样式
- ✅ LocationSource接口实现
下一篇我们将学习POI搜索功能。
班级
https://developer.huawei.com/consumer/cn/training/classDetail/fd34ff9286174e848d34cde7f512ce22?type=1%3Fha_source%3Dhmosclass&ha_sourceId=89000248
源码地址
https://gitcode.com/daleishen/gaodehmjiaocheng.git