代码改变世界

[鸿蒙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