位置消息
IMKit 基于高德地图 SDK 提供了位置消息,实现了应用地图预览功能。
- SDK 默认发送的消息包含位置消息内容对象
LocationMessage
(类型标识:RC:LBSMsg
)。
提示
IMKit 默认会话页面未启用位置功能。如需要使用位置功能,需要自定义位置插件开发。下面提供了示例代码进行参考。

发送位置消息代码示例
通过自定义位置消息插件,在扩展面板里会自动生成位置消息入口。用户点击输入栏右侧 +
号按钮可展开扩展面板,点击位置图标,即可发送位置消息。
示例基于鸿蒙 Map Kit SDK,集成于配置请参照鸿蒙开发文档。
自定义位置插件
LocationPlugin
是提供给开发者借鉴的位置插件示例代码。开发者需要在 sendLocation
方法中编写跳转 LocationPage
的代码。
import { ArrayChecker, ConversationIdentifier } from '@rongcloud/imlib';
import { IBoardPlugin } from '@rongcloud/imkit';
import { PermissionsUtil } from '../../../utils/PermissionsUtil';
import { LocationParams, SelectLocationType } from '../model/LocationParams';
import { common, Permissions } from '@kit.AbilityKit';
/**
* 加号扩展栏的位置插件
* @version 1.0.0
*/
export class LocationPlugin implements IBoardPlugin {
obtainTitle(context: Context): ResourceStr {
return $r("app.string.rc_location");
}
obtainImage(context: Context): ResourceStr {
return $r("app.media.rc_input_bar_plugin_location");
}
onClick(context: Context, conId: ConversationIdentifier): void {
//先检查收否授权地图权限,没有授权的话去设置权限
const permissionArray: Permissions[] = [
'ohos.permission.LOCATION',
'ohos.permission.APPROXIMATELY_LOCATION'
];
PermissionsUtil.checkPermissions(permissionArray)
.then((array: Permissions[]) => {
//已经授权
if (array.length === 0) {
// 发送位置页面
this.sendLocation(conId);
} else {
// 没授权,需要申请权限
PermissionsUtil.requestPermissionsFromUser(getContext(this) as common.UIAbilityContext, permissionArray)
.then((permissions: Array<Permissions>) => {
if (ArrayChecker.isValid(permissions)) {
PermissionsUtil.requestPermissionOnSetting(getContext(this) as common.UIAbilityContext,
permissionArray);
} else {
// 发送位置页面
this.sendLocation(conId);
}
});
}
});
}
/**
* 发送位置页面
*/
private sendLocation(conId: ConversationIdentifier) {
let param: LocationParams = { conId: conId, flag: SelectLocationType.Send }
// 跳转到 LocationPage 页面,携带 LocationParams 参数。
}
onFilter(conId: ConversationIdentifier): boolean {
return true;
}
}
添加自定义插件到扩展面板
添加位置插件到最后位置。
let plugin = new LocationPlugin()
RongIM.getInstance().conversationService().addBoardPlugin(plugin)
也可以把位置插件插入到指定位置。
let plugin = new LocationPlugin()
let filePluginIndex = 1
RongIM.getInstance().conversationService().replaceBoardPlugin(filePluginIndex, plugin)
发送位置/查看位置页面示例
LocationPage
是提供给开发者借鉴的发送位置/查看位置示例代码。
- 支持通过自定义位置插件跳转到此页面,选择位置并发送
LocationMessage
,见returnSelectLocation
方法。 - 支持通过点击
LocationMessage
的 UI 跳转到此页面,查看位置。见 自定义位置消息UI。 - 需要 App 侧在
backPage
方法处理返回上个页面的逻辑。
import {
ConversationIdentifier,
LocationCoordinateType,
LocationMessage,
Message,
RCTitleBar,
RongIM,
StringChecker
} from '@rongcloud/imkit';
import { LocationData } from '../model/LocationData';
import { LocationParams } from '../model/LocationParams';
import { map, mapCommon, MapComponent, site, staticMap } from '@kit.MapKit';
import { AsyncCallback, BusinessError } from '@kit.BasicServicesKit';
import { geoLocationManager } from '@kit.LocationKit';
import { router } from '@kit.ArkUI';
import { common, Want } from '@kit.AbilityKit';
import { connection } from '@kit.NetworkKit';
import { image } from '@kit.ImageKit';
import { buffer } from '@kit.ArkTS';
@Entry
@Component
export struct LocationPage {
// 网络状态
@State isNetwork: boolean = false;
private netCon: connection.NetConnection | undefined;
// 地图属性
private mapOptions?: mapCommon.MapOptions;
private callback?: AsyncCallback<map.MapComponentController>;
private mapController?: map.MapComponentController;
private mapEventManager?: map.MapEventManager;
// 当前位置
private myLatLng: mapCommon.LatLng = LocationHelper.defaultLatLng;
// 展示的周边列表数据
@State locationList: Array<LocationData> = [];
private pageIndexDefault: number = 1;
// 页码
private pageIndex: number = this.pageIndexDefault;
// 每页数量
private pageSize: number = 20;
private locationIndexDefault: number = 0;
// 选择的位置下标
@State
private locationIndex: number = -1;
// 是否在获取周边数据
private nearbySearchType: boolean = false;
// app内主动移动地图的次数,如果是app内主动移动地图,不需要在地图移动后获取位置信息和周边
// 初始值为1,是在地图初始化后,会自动回调一下地图移动的回调
private appRemoveNum: number = 1;
@State
private selectAddress: string = '';
// 是否只展示地图
private isOnlyShowMap: boolean = false;
// 地图所需参数
private locationParams?: LocationParams;
// 错误码
private errorCode: string = ''
// 会话ID
private conId: ConversationIdentifier = new ConversationIdentifier();
// 标题栏
@State model: RCTitleBar.Model = new RCTitleBar.Model()
.setTitleName(getContext().resourceManager.getStringByNameSync('rc_location'))
.setTitleFontSize(18)
.setLeftIcon($r("app.media.rc_title_bar_back"))
.setRightTitleName(getContext().resourceManager.getStringByNameSync('rc_chat_send'))
.setRightTitleFontColor(this.isNetwork ? $r('app.color.rc_color_0195ff') : $r('app.color.rc_color_C7CCD4'))
.setOnLeftClickListener(() => {
this.backPage()
})
.setOnRightClickListener(() => {
if (!this.isNetwork) {
return;
}
if (this.errorCode === '3301100') {
return;
}
if (this.locationIndex === -1) {
return;
}
if (!this.locationList.length) {
return;
}
this.returnSelectLocation();
})
aboutToAppear(): void {
// 网络监听
this.networkListen()
// 获取路由参数
if (router.getParams()) {
this.locationParams = router.getParams() as LocationParams;
if (this.locationParams.conId && this.locationParams.flag == 1) {
this.conId.targetId = this.locationParams.conId?.targetId!
this.conId.conversationType = this.locationParams.conId?.conversationType!
} else if (this.locationParams.locationData && this.locationParams.flag == 2) {
if (this.locationParams.locationData) {
// 有位置数据,只展示地图和标记当前要显示的位置
this.isOnlyShowMap = !this.isOnlyShowMap;
this.model.setRightTitleName('')
}
}
}
// 地图初始化参数,设置地图中心点坐标及层级
this.initMapData()
}
private initMapData() {
// 地图参数
this.mapOptions = LocationHelper.getMapInitOption(this.isOnlyShowMap);
// 地图初始化的回调
this.callback = async (err, mapController) => {
if (!err) {
// 获取地图的控制器类,用来操作地图
this.mapController = mapController;
this.mapEventManager = this.mapController.getEventManager();
this.mapEventManager.on('error', (err: BusinessError) => {
});
// 地图加载事件
let mapLoadCallback = () => {
//地图加载完成获取定位信息,
if (!this.isOnlyShowMap) {
this.getMyLocation();
} else {
if (this.mapController !== undefined) {
LocationHelper.animateCamera(this.locationParams?.locationData?.latitude!,
this.locationParams?.locationData?.longitude!, this.mapController);
LocationHelper.addPointAnnotation(this.locationParams?.locationData?.latitude!,
this.locationParams?.locationData?.longitude!, this.locationParams?.locationData?.name!,
this.mapController);
}
}
};
this.mapEventManager.on("mapLoad", mapLoadCallback);
// 相机移动结束事件
let cameraIdleCallback = () => {
// 如果是app内主动移动地图,不需要获取信息
if (this.appRemoveNum > 0) {
this.appRemoveNum--;
return;
}
//获取地图移动后的中心点
let position: mapCommon.CameraPosition = this.mapController?.getCameraPosition() as mapCommon.CameraPosition;
this.getLocationDetail(position.target.latitude, position.target.longitude);
};
if (!this.isOnlyShowMap) {
this.mapEventManager.on("cameraIdle", cameraIdleCallback);
}
// 我的位置按钮点击事件
let myLocationButtonClickCallback = () => {
this.getMyLocation();
};
this.mapEventManager.on("myLocationButtonClick", myLocationButtonClickCallback);
// 启用我的位置图层
this.mapController.setMyLocationEnabled(!this.isOnlyShowMap);
// 启用我的位置按钮
this.mapController.setMyLocationControlsEnabled(!this.isOnlyShowMap);
} else {
}
};
}
/**
* 获取当前位置,并移动地图到当前位置
*/
private getMyLocation() {
try {
let currentLocation = geoLocationManager.getCurrentLocation();
if (!currentLocation) {
return;
}
// 获取用户位置坐标
currentLocation.then((location: geoLocationManager.Location) => {
// 设置用户的位置
// TODO location参数需使用WGS84坐标系。
this.mapController?.setMyLocation(location);
let gcj02Position = LocationHelper.wgs84ToGcj02(location.latitude, location.longitude);
this.myLatLng.latitude = gcj02Position.latitude;
this.myLatLng.longitude = gcj02Position.longitude;
if (this.mapController !== undefined) {
this.appRemoveNum++;
LocationHelper.animateCamera(this.myLatLng.latitude, this.myLatLng.longitude, this.mapController);
}
this.getLocationDetail(this.myLatLng.latitude, this.myLatLng.longitude);
})
this.errorCode = '';
} catch (e) {
this.errorCode = e.code;
if (this.errorCode === '3301100') {
this.model.setRightTitleFontColor($r('app.color.rc_color_C7CCD4'))
this.showLocationDialog();
}
}
}
private showLocationDialog() {
AlertDialog.show({
title: '温馨提示',
message: '位置开关已被关闭,打开后才能正常发送',
autoCancel: true,
alignment: DialogAlignment.Center,
primaryButton: {
value: '去开启',
fontColor: $r('app.color.rc_color_0195ff'),
action: () => {
this.openSetting();
}
},
cornerRadius: 12,
width: '80%',
});
}
private openSetting() {
let context = getContext(this) as common.UIAbilityContext;
let want: Want = {
bundleName: 'com.huawei.hmos.settings', //设置应用bundleName
abilityName: 'com.huawei.hmos.settings.MainAbility', //设置应用abilityName
uri: "location_manager_settings"
}
context.startAbility(want)
}
/**
* 逆地理编码,根据当前位置经纬度获取详细信息
*/
private getLocationDetail(latitude: number, longitude: number) {
//重置显示数据
this.errorCode = '';
this.locationList = [];
this.pageIndex = this.pageIndexDefault;
this.locationIndex = this.locationIndexDefault;
LocationHelper.reverseGeocode(latitude, longitude).then((result: LocationData) => {
this.selectAddress = result.name;
this.locationList.push(result);
this.nearbySearch();
})
}
/**
* 周边搜索
*/
private nearbySearch() {
if (this.locationList.length > 0 && this.locationList.length > 0) {
LocationHelper.nearbySearch(this.locationList[0].latitude, this.locationList[0].longitude, this.pageIndex,
this.pageSize)
.then((value: Array<LocationData>) => {
this.locationList.push(...value);
if (this.nearbySearchType) {
this.nearbySearchType = !this.nearbySearchType;
}
});
}
}
// 页面每次显示时触发一次,包括路由过程、应用进入前台等场景,仅@Entry装饰的自定义组件生效
onPageShow(): void {
// 将地图切换到前台
if (this.mapController !== undefined) {
this.mapController.show();
}
if (this.errorCode === '3301100') {
if (connection.hasDefaultNetSync()) {
this.isNetwork = true;
this.model.setRightTitleFontColor($r('app.color.rc_color_0195ff'))
//获取地图移动后的中心点
let position: mapCommon.CameraPosition = this.mapController?.getCameraPosition() as mapCommon.CameraPosition;
if (position) {
this.getLocationDetail(position.target.latitude, position.target.longitude);
}
}
}
}
// 页面每次隐藏时触发一次,包括路由过程、应用进入后台等场景,仅@Entry装饰的自定义组件生效。
onPageHide(): void {
// 将地图切换到后台
if (this.mapController !== undefined) {
// TODO 模拟器加载地图调用此api会崩溃,先抛出
try {
this.mapController.hide();
} catch (e) {
console.error(JSON.stringify(e));
}
}
}
build() {
Column() {
RCTitleBar({ model: this.model })
RelativeContainer() {
// 调用MapComponent组件初始化地图
MapComponent({ mapOptions: this.mapOptions, mapCallback: this.callback })
if (!this.isOnlyShowMap) {
Row() {
Image($r('sys.media.ohos_ic_public_location'))
.width(20)
.height(20)
Text(this.selectAddress)
.layoutWeight(1)
}
.backgroundColor(Color.White)
.width('80%')
.padding(10)
.margin({ top: 10 })
.alignRules({
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
Image($r('app.media.rc_map_location_mark'))
.objectFit(ImageFit.Auto)
.width(24)
.height(58)
.alignRules({
center: { anchor: '__container__', align: VerticalAlign.Center },
middle: { anchor: '__container__', align: HorizontalAlign.Center }
})
}
}
.width('100%')
.layoutWeight(2)
if (!this.isOnlyShowMap) {
List() {
ForEach(this.locationList, (item: LocationData, index: number) => {
ListItem() {
Row() {
Column() {
Text(item.name)
.layoutWeight(1)
.width('100%')
Text(item.address)
.width('100%')
.fontColor(Color.Grey)
.fontSize(14)
.maxLines(1)
.textOverflow({ overflow: TextOverflow.Ellipsis })
.margin({ top: 10 })
.visibility(item.address ? Visibility.Visible : Visibility.None)
}
.padding(10)
.height(70)
.layoutWeight(1)
Image($r('sys.media.ohos_ic_public_ok_filled'))
.width(15)
.height(15)
.visibility(index === this.locationIndex ? Visibility.Visible : Visibility.None);
}
}
.margin({ right: 20 })
.onClick(() => {
this.locationIndex = index;
this.selectAddress = item.name
this.appRemoveNum++;
LocationHelper.animateCamera(item.latitude, item.longitude, this.mapController!);
})
}, (item: LocationData, index: number) => {
return `${item.latitude}${item.longitude}${index}`;
})
}
.divider({ strokeWidth: 1 })
.onScrollVisibleContentChange((start: VisibleListContentInfo, end: VisibleListContentInfo) => {
//滑动到最后一个item,查询后边的数据
if (end.index !== this.locationList.length - 1) {
return;
}
this.searchNextPage();
})
.layoutWeight(1)
}
}
}
// 返回上个页面
private backPage() {
// 处理返回上个页面
}
/**
* 继续搜索下一页数据
*/
private searchNextPage() {
if (!this.nearbySearchType) {
this.nearbySearchType = !this.nearbySearchType;
this.pageIndex++;
this.nearbySearch();
}
}
/**
* 返回选择的位置
*/
private returnSelectLocation() {
LocationHelper.getMapImage(this.locationList[this.locationIndex].latitude,
this.locationList[this.locationIndex].longitude).then((value: PixelMap) => {
// 返回数据给上个页面
let selectLocation: LocationData = this.locationList[this.locationIndex];
ImageUtil.pixelMapToBase64(value).then((base64: string) => {
selectLocation.img = base64;
let locationMessage: LocationMessage = new LocationMessage();
locationMessage.latitude = selectLocation.latitude;
locationMessage.longitude = selectLocation.longitude;
locationMessage.poi = selectLocation.name;
locationMessage.thumbnailBase64 = selectLocation.img!;
locationMessage.type = LocationCoordinateType.GCJ02;
let msg = new Message(this.conId, locationMessage);
RongIM.getInstance().messageService().sendMessage(msg);
router.back();
});
});
}
private networkListen() {
this.netCon = connection.createNetConnection();
this.netCon.register((error: BusinessError) => {
if (error) {
console.log('networkListen fail' + JSON.stringify(error))
return;
}
});
this.netCon.on('netAvailable', (data: connection.NetHandle) => {
console.info("Succeeded to get netAvailable: " + JSON.stringify(data));
if (connection.hasDefaultNetSync()) {
this.isNetwork = true;
this.model.setRightTitleFontColor($r('app.color.rc_color_0195ff'))
//获取地图移动后的中心点
let position: mapCommon.CameraPosition = this.mapController?.getCameraPosition() as mapCommon.CameraPosition;
if (position) {
this.getLocationDetail(position.target.latitude, position.target.longitude);
}
}
});
// 订阅网络丢失事件
this.netCon.on('netLost', (data: connection.NetHandle) => {
if (connection.getAllNetsSync().length == 0) {
this.isNetwork = false;
this.model.setRightTitleFontColor($r('app.color.rc_color_C7CCD4'))
}
console.info("Succeeded to get netLost: " + JSON.stringify(data));
});
this.netCon.on('netCapabilitiesChange', (data: connection.NetCapabilityInfo) => {
console.info("Succeeded to get netCapabilitiesChange: " + JSON.stringify(data));
});
this.netCon.on('netUnavailable', () => {
console.info("Succeeded to get unavailable net event");
this.isNetwork = false;
this.model.setRightTitleFontColor($r('app.color.rc_color_C7CCD4'))
});
}
aboutToDisappear(): void {
this.netCon?.unregister((error: BusinessError) => {
console.log(JSON.stringify(error));
});
}
}
export class LocationHelper {
// 地图默认中心点纬度-北京
private static centerDefaultLat: number = 39.9042;
// 地图默认中心点经度-北京
private static centerDefaultLon: number = 116.4074;
public static defaultLatLng: mapCommon.LatLng =
{ latitude: LocationHelper.centerDefaultLat, longitude: LocationHelper.centerDefaultLon };
// 地图默认展示的层级
private static centerDefaultZoom: number = 10;
// 移动地图到某一点的图层
private static animateCameraDefaultZoom: number = 15;
// 输入语言
private static language: string = "zh";
// 逆地理编码搜索半径
private static reverseGeocodeRadius: number = 10;
// 周边搜索半径
private static nearbySearchRadius: number = 5000;
// 地图移动到指定经纬度的时长
private static animateCameraDuration: number = 500;
// 静态图的图层
private static mapImageZoom: number = 17;
// 静态图的宽高
private static mapImageWidth: number = 300;
private static mapImageHeight: number = 200;
// 静态图的比例
private static mapImageScale: number = 1;
/**
* 地图初始化参数
* @returns
*/
public static getMapInitOption(isOnlyShowMap: boolean): mapCommon.MapOptions {
let mapOptions: mapCommon.MapOptions = {
position: {
target: LocationHelper.defaultLatLng,
zoom: LocationHelper.centerDefaultZoom
},
// 设置地图深色模式
dayNightMode: mapCommon.DayNightMode.AUTO,
// 是否展示我的位置按钮,默认值:false
myLocationControlsEnabled: !isOnlyShowMap,
// 是否展示指南针控件,默认值:true
compassControlsEnabled: false,
// 是否展示缩放控件,默认值:true
zoomControlsEnabled: false
};
return mapOptions;
}
/**
* 经纬度转换,wgs84转为gcj02
*/
public static wgs84ToGcj02(lat: number, lon: number): mapCommon.LatLng {
let wgs84Position: mapCommon.LatLng = {
latitude: lat,
longitude: lon
};
// 转换经纬度坐标
let gcj02Position: mapCommon.LatLng =
map.convertCoordinateSync(mapCommon.CoordinateType.WGS84, mapCommon.CoordinateType.GCJ02, wgs84Position);
return gcj02Position;
}
/**
* 逆地理编码,最终返回页面列表显示的数据
* @param lat
* @param lon
* @returns
*/
public static reverseGeocode(lat: number, lon: number): Promise<LocationData> {
return new Promise((resolve) => {
let params: site.ReverseGeocodeParams = {
// 位置经纬度
location: {
latitude: lat,
longitude: lon
},
isExtension: true,
language: LocationHelper.language,
radius: LocationHelper.reverseGeocodeRadius
};
site.reverseGeocode(params).then((result: site.ReverseGeocodeResult) => {
let addressName: string = "";
let addressLatitude: number = lat;
let addressLongitude: number = lon;
let pois: Array<site.ReverseGeocodePoi> = result.pois as Array<site.ReverseGeocodePoi>;
if (pois) {
addressName = StringChecker.getStringSafety(pois[0].name);
addressLatitude = pois[0].location?.latitude as number;
addressLongitude = pois[0].location?.longitude as number;
} else {
let aois: Array<site.Aoi> = result.aois as Array<site.Aoi>;
if (aois) {
addressName = StringChecker.getStringSafety(aois[0].name);
addressLatitude = aois[0].location?.latitude as number;
addressLongitude = aois[0].location?.longitude as number;
} else {
let roads: Array<site.Road> = result.roads as Array<site.Road>;
if (roads) {
addressName = StringChecker.getStringSafety(roads[0].name);
addressLatitude = roads[0].location?.latitude as number;
addressLongitude = roads[0].location?.longitude as number;
} else {
addressName = result.addressDescription;
}
}
}
let resultData: LocationData = { latitude: addressLatitude, longitude: addressLongitude, name: addressName };
resolve(resultData);
});
});
}
/**
* 周边搜索
* @param lat
* @param lon
* @param pageIndex
* @param pageSize
*/
public static nearbySearch(lat: number, lon: number, pageIndex: number,
pageSize: number): Promise<Array<LocationData>> {
return new Promise((resolve) => {
let list: Array<LocationData> = [];
let params: site.NearbySearchParams = {
// 经纬度坐标
location: {
latitude: lat,
longitude: lon
},
// 指定地理位置的范围半径
radius: LocationHelper.nearbySearchRadius,
language: LocationHelper.language,
pageIndex: pageIndex,
pageSize: pageSize
};
// 返回周边搜索结果
site.nearbySearch(params).then((result: site.NearbySearchResult) => {
result.sites?.forEach((item: site.Site) => {
list.push({
latitude: item.location?.latitude as number,
longitude: item.location?.longitude as number,
name: StringChecker.getStringSafety(item.name),
address: StringChecker.getStringSafety(item.formatAddress)
});
});
resolve(list);
});
});
}
/**
* 根据关键词搜索地点
* @param searchKey
* @returns
*/
public static searchByTextAndLocation(searchKey: string, location?: mapCommon.LatLng): Promise<Array<LocationData>> {
return new Promise((resolve) => {
let list: Array<LocationData> = [];
let params: site.SearchByTextParams = {
// 指定关键字
query: searchKey,
location: location,
language: LocationHelper.language
};
// 返回关键字搜索结果
site.searchByText(params).then((result: site.SearchByTextResult) => {
result.sites?.forEach((item: site.Site) => {
list.push({
latitude: item.location?.latitude as number,
longitude: item.location?.longitude as number,
name: StringChecker.getStringSafety(item.name),
address: StringChecker.getStringSafety(item.formatAddress)
});
});
resolve(list);
});
});
}
/**
* 移动地图到指定的经纬度
* @param lat
* @param lon
* @param mapController
*/
public static animateCamera(lat: number, lon: number, mapController: map.MapComponentController) {
let latLng: mapCommon.LatLng = {
latitude: lat,
longitude: lon
};
let cameraUpdate: map.CameraUpdate = map.newLatLng(latLng, LocationHelper.animateCameraDefaultZoom);
// 以动画方式移动地图相机
mapController.animateCamera(cameraUpdate, LocationHelper.animateCameraDuration);
}
/**
* 获取地图静态图
* @param lat
* @param lon
* @returns
*/
public static getMapImage(lat: number, lon: number): Promise<PixelMap> {
return new Promise((resolve) => {
// 设置静态图标记参数
let markers: Array<staticMap.StaticMapMarker> = [{
location: {
latitude: lat,
longitude: lon
},
defaultIconSize: staticMap.IconSize.TINY
}];
// 拼装静态图参数
let staticMapOptions: staticMap.StaticMapOptions = {
location: {
latitude: lat,
longitude: lon
},
zoom: LocationHelper.mapImageZoom,
imageWidth: LocationHelper.mapImageWidth,
imageHeight: LocationHelper.mapImageHeight,
scale: LocationHelper.mapImageScale,
markers: markers
};
// 获取静态图
staticMap.getMapImage(staticMapOptions).then((value: PixelMap) => {
resolve(value);
});
});
}
/**
* 在地图上添加标记
* @param lat
* @param lon
* @param mapController
*/
public static async addMarker(lat: number, lon: number, mapController: map.MapComponentController) {
if (mapController) {
//清除所有的圆、标记、折线等覆盖物
mapController.clear();
let markerOptions: mapCommon.MarkerOptions = {
position: {
latitude: lat,
longitude: lon
},
rotation: 0,
visible: true,
zIndex: 0,
alpha: 1,
anchorU: 0.5,
anchorV: 1,
clickable: true,
draggable: true,
flat: false,
icon: $r('app.media.rc_portrait_person_default')
};
//给地图中心点添加标记
await mapController.addMarker(markerOptions);
}
}
/**
* 增加点注释
*/
public static addPointAnnotation(lat: number, lon: number, name: string, mapController: map.MapComponentController) {
let pointAnnotationOptions: mapCommon.PointAnnotationParams = {
// 定义点注释图标锚点
position: {
latitude: lat,
longitude: lon
},
// 定义点注释名称与地图poi名称相同时,是否支持去重
repeatable: true,
// 定义点注释的碰撞规则
collisionRule: mapCommon.CollisionRule.NAME,
// 定义点注释的标题,数组长度最小为1,最大为3
titles: [{
// 定义标题内容
content: name,
// 定义标题字体颜色
color: getContext().resourceManager.getColorSync($r("app.color.rc_color_0E1012")),
// 定义标题字体大小
fontSize: 16,
// 定义标题描边颜色
// strokeColor: 0xFFFFFFFF,
// 定义标题描边宽度
strokeWidth: 2,
// 定义标题字体样式
fontStyle: mapCommon.FontStyle.ITALIC
}
],
// 定义点注释的图标,图标存放在resources/rawfile
icon: "",
// 定义点注释是否展示图标
showIcon: true,
// 定义点注释的锚点在水平方向上的位置
anchorU: 0.5,
// 定义点注释的锚点在垂直方向上的位置
anchorV: 1,
// 定义点注释的显示属性,为true时,在被碰撞后仍能显示
forceVisible: true,
// 定义碰撞优先级,数值越大,优先级越低
priority: 3,
// 定义点注释展示的最小层级
minZoom: 2,
// 定义点注释展示的最大层级
maxZoom: 22,
// 定义点注释是否可见
visible: true,
// 定义点注释叠加层级属性
zIndex: 10
};
mapController.addPointAnnotation(pointAnnotationOptions);
}
}
export class ImageUtil {
/**
* 图像数据转为 Base64
*/
public static pixelMapToBase64(pixelMap: PixelMap): Promise<string> {
return new Promise((resolve) => {
const imagePackerApi: image.ImagePacker = image.createImagePacker();
let packOpts: image.PackingOption = { format: 'image/jpeg', quality: 100 };
imagePackerApi.packing(pixelMap, packOpts).then((data: ArrayBuffer) => {
let buf: buffer.Buffer = buffer.from(data);
let base64 = buf.toString('base64', 0, buf.length);
resolve(base64);
});
});
}
}
示例代码中引用到的类
权限管理类
import { abilityAccessCtrl, common, Permissions } from '@kit.AbilityKit';
import { bundleManager } from '@kit.AbilityKit';
import { ArrayChecker, ObjectChecker } from '@rongcloud/imlib';
export class PermissionsUtil {
/**
* 检查某个权限是否已授权
* @param permission 需要检查的权限
* @returns 授权状态
*/
public static async checkPermission(permission: Permissions): Promise<abilityAccessCtrl.GrantStatus> {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
let grantStatus: abilityAccessCtrl.GrantStatus = abilityAccessCtrl.GrantStatus.PERMISSION_DENIED;
// 获取应用程序的accessTokenID
let tokenId: number = await PermissionsUtil.getAccessTokenId();
// 校验应用是否被授予权限
try {
grantStatus = atManager.checkAccessTokenSync(tokenId, permission);
} catch (error) {
// 异常
}
return grantStatus;
}
/**
* 检查一个 Array 里未授权的权限
* @param checkPermissionArray 需要检查的权限 Array
* @returns 返回未授权的 Array
*/
public static async checkPermissions(checkPermissionArray: Array<Permissions>): Promise<Array<Permissions>> {
const deniedPermissions: Array<Permissions> = new Array();
if (ArrayChecker.isValid(checkPermissionArray)) {
for (let checkPermissionArrayElement of checkPermissionArray) {
if (!ObjectChecker.isValid(checkPermissionArrayElement)) {
continue;
}
let grantStatus: abilityAccessCtrl.GrantStatus =
await PermissionsUtil.checkPermission(checkPermissionArrayElement);
if (grantStatus === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
//未授权
deniedPermissions.push(checkPermissionArrayElement);
}
}
}
return deniedPermissions;
}
/**
* 动态请求权限
* @param context UIAbilityContext
* @param permissions 需要申请的权限
* @returns 被拒绝的权限
*/
public static async requestPermissionsFromUser(context: common.UIAbilityContext,
permissions: Array<Permissions>): Promise<Array<Permissions>> {
//被拒绝授权的数组
const deniedPermissions: Array<Permissions> = new Array();
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
// requestPermissionsFromUser会判断权限的授权状态来决定是否唤起弹窗
let data = await atManager.requestPermissionsFromUser(context, permissions)
let resultPermissions: Array<string> = data.permissions;
let grantStatus: Array<number> = data.authResults;
let length: number = grantStatus.length;
for (let i = 0; i < length; i++) {
if (grantStatus[i] === abilityAccessCtrl.GrantStatus.PERMISSION_DENIED) {
// 用户拒绝授权,提示用户必须授权才能访问当前页面的功能,并引导用户到系统设置中打开相应的权限
deniedPermissions.push(resultPermissions[i] as Permissions);
}
}
return deniedPermissions;
}
/**
* 二次向用户申请授权,直接拉起权限设置弹框,引导用户授予权限
* @param context
* @param permissions
*/
public static requestPermissionOnSetting(context: common.UIAbilityContext, permissions: Array<Permissions>) {
let atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();
atManager.requestPermissionOnSetting(context, permissions);
}
/**
* 获取当前应用的 AccessTokenId
* @returns AccessTokenId
*/
public static async getAccessTokenId(): Promise<number> {
// 获取应用程序的accessTokenID
let tokenId: number = 0;
try {
let bundleInfo: bundleManager.BundleInfo =
await bundleManager.getBundleInfoForSelf(bundleManager.BundleFlag.GET_BUNDLE_INFO_WITH_APPLICATION);
let appInfo: bundleManager.ApplicationInfo = bundleInfo.appInfo;
tokenId = appInfo.accessTokenId;
} catch (error) {
// 异常
}
return tokenId;
}
}
LocationParams
、 SelectLocationType
import { ConversationIdentifier } from '@rongcloud/imlib';
import { LocationData } from './LocationData';
export interface LocationParams {
locationData?: LocationData;
conId?: ConversationIdentifier;
flag: number;
}
export enum SelectLocationType {
Send = 1,
Look = 2,
}
LocationData
export class LocationData {
latitude: number = 0.0;
longitude: number = 0.0;
name: string = '';
address?: string = '';
// 地图的静态图像PixelMap转的base64字符串
img?: string = '';
}
处理位置消息点击事件
位置消息与位置消息的 UI 定义在了Kit SDK中,所以需要 App 通过设置消息点击事件的方式来处理。 设置消息点击事件,建议在 SDK init之后设置。
let msgClickListener: MessageClickListener = {
onMessageClick(message) {
if (message.objectName === LocationMessageObjectName) {
// 地图消息跳转地图页面查看位置
let messageContent: LocationMessage = message.content as LocationMessage;
let locationData: LocationData = new LocationData();
locationData.latitude = messageContent.latitude;
locationData.longitude = messageContent.longitude;
locationData.name = messageContent.poi;
let param: LocationParams = { locationData: locationData, flag: SelectLocationType.Look }
// 跳转到查看位置页面
return true;
}
return false;
}
};
RongIM.getInstance().conversationService().addMessageClickListener(msgClickListener);
定制化
自定义位置消息的 UI
位置消息使用 LocationMessageItemProvider
模板展示在消息列表中。
如果需要调整内置消息样式,需继承 BaseMessageItemProvider<LocationMessage>
自行实现消息展示模板类,详见自定义Provider。
调用下面的接口将该自定义模板提供给 SDK,objectName
传 LocationMessageObjectName
。
import { LocationMessageObjectName, RongIM } from "@rongcloud/imkit";
// 注册自定义位置消息 provider 给 IMKit
RongIM.getInstance().conversationService().addMessageItemProvider(LocationMessageObjectName, new CustomLocationMessageItemProvider())