实战|uniapp模仿微信实现发送位置消息,解决滚动页面地图层级冲突

前言

在即时通讯应用中,虽然发送位置信息不是核心功能,但在特定场景下,这个功能仍然非常有用。

本文将介绍如何在 uniapp 中实现位置信息的发送和展示,特别是在遇到地图层级问题时的解决方案。

以下内容均基于 uniapp 打包 App 端。

常规解决方案带来的问题

在 uniapp 中,通常使用map组件来展示位置信息。但在打包为 App 端后,map组件的层级问题会导致它无法随着聊天页面滚动。就像下图的情况:

在这个方案中,有在文档上看到描述说可以采用cover-view去解决在滚动组件中map层级过高的问题,但是经过折腾应该还是对 uniapp 的了解不够,没有解决层级过高的问题,因此换了下面的方案,也算是一个新的路子,希望能够对看到这篇文档的你有所帮助。

采用的方案

静态地图方案

静态地图方案的核心其实是用一张地图的静态图片,替换了map组件,图片在scoll-list中不存在有层级过高的情况。而静态地图的产生则是调用了地图厂商所提供的静态地图 api,在调用生成的静态地图 api 时通过 url 传参的形式,将要展示的坐标经纬度传入,并且根据地图厂商规则去传入其他扩展参数,从而达到预期想要生成的静态地图效果。在本次我的实现中,我采用的是高德地图的 api,其他如百度,腾讯类似。

在高德静态地图 api 中,你可以除可传入经纬度坐标,也支持返回的图片宽高,这里我贴一下具体官方文档地址,供大家详细研究静态地图的使用:

高德静态地图

功能实现

在接下来的篇幅里面,我将描述一下,我在实现位置消息发送,消息页面展示位置气泡,以及点击消息气泡展示详细位置的实现过程,以及遇到的问题。

在实现之前我们需要先在 uniApp 官网查看相关获取位置、选择位置、查看位置相关 api 的用法以及注意事项。

第一步:实现发送位置

1)在mainifest.json文件下的App模块配置中勾选Geolocation(定位)

在定位中你可以选择系统定位或者是高德、百度等厂商的系统定位,这里为了延时,就直接选择系统定位。

2)点击发送位置获取系统定位授权
js 复制代码
getLocation() {
      uni.getLocation({
        // type: 'wgs84',
        type: 'gcj02',
        success: (res) => {
          this.location = { ...res };
          console.log('当前位置:' + JSON.stringify(res));
        },
        fail: (err) => {
          console.error(err);
          // 这里可以处理权限被拒绝的情况
          if (err.errMsg === 'getLocation:fail auth deny') {
            // 引导用户打开权限设置
            uni.showModal({
              title: '提示',
              content: '需要获取您的位置信息,请到设置中打开相关权限',
              success: function (res) {
                if (res.confirm) {
                  // 打开设置页面
                  uni.openSetting({
                    success: function (res) {
                      console.log(res.authSetting);
                      // res.authSetting = { "scope.userLocation": true } 表示已获得权限
                    },
                  });
                }
              },
            });
          }
        },
      });
    },
3)授权后调用uni.chooseLocation打开进入地图选择位置。

此 api 在调用拉起时会发现进入的页面并不会给你一个地图页面以及地点联想,这是因为还需要在App权限模块中勾选MapsMaps下有高德百度Google三家,而我这里就选高德地图。

用户名的获取:

appkey_android key 的获取:

你需要在高德开放平台进行操作:

进入高德控制台应用管理我的应用创建新应用添加Key选择服务平台获取SHA1码

在填写一系列指定信息后,即可成功创建。

如果你不知道SHA1码的获取,在下文中也会提到对应 Mac 平台获取的方式。

appkey_ios key 的获取:

同安卓步骤只不过 iOS 不需要获取SHA1码,仅需要填写Bundle ID

完成以上步骤后,重新打包自定义调试基座,确保将地图原生插件依赖进去。

重新调用uni.chooseLocation后发现正常的展示了当下前位置,以及附近位置,选择完成后即可获取勾选的位置经纬度,位置名称,详细位置。

4) 发送位置消息

通过调用环信 uniApp api 发送位置消息来实现位置消息的发送

js 复制代码
let option = {
  chatType: 'singleChat',
  type: 'loc',
  to: 'username',
  addr: this.location.address || '未知地址',
  buildingName: this.location.name || '未知建筑名称',
  lat: this.location.latitude,
  lng: this.location.longitude,
};
let msg = WebIM.message.create(option);
conn
  .send(msg)
  .then((res) => {
    console.log('Send message success', res);
  })
  .catch((e) => {
    console.log('Send message fail', e);
  });

该 api 相关文档:
环信-发送位置消息

第二步:渲染展示位置消息气泡

1) 创建高德地图 Web 平台 Key

静态地图的使用需要创建 Web 平台的 Key,该 Key 会在拼接静态地图 URL 时用到。

sh 复制代码
https://restapi.amap.com/v3/staticmap?location=116.481485,39.990464&zoom=10&size=750*300&markers=mid,,A:116.481485,39.990464&key=<用户的key>
2) 拿经纬度置换可用的静态地图资源图片

通过解析发送或者接收到的位置消息中的经纬度,调用高德地图静态地图 api,从而获取对应坐标的静态地图。

msgBody 即为环信收发位置消息的消息体。

js 复制代码
    <img
      class="map_image_container"
      :src="`${AMAP_API_URL}${location}&zoom=10&size=400*200&scale=1&markers=mid,,A:${msgBody.lng},${msgBody.lat}&key=${AMAP_KEY}`"
    />

const AMAP_KEY = 'YOUR KEY';
const AMAP_API_URL = 'https://restapi.amap.com/v3/staticmap?';
const location = () => {
      return `location=${this.msgBody.lng},${this.msgBody.lat}`;
    },
3)展示位置名以及详细地址
js 复制代码
   <view class="location_msg_container" @click="openMap">
    <u--text
      :lines="1"
      :text="msgBody.buildingName ? msgBody.buildingName : '未知建筑'"
    ></u--text>
    <u--text
      :lines="1"
      :text="msgBody.addr ? msgBody.addr : '未知建筑'"
    ></u--text>
    <img
      class="map_image_container"
      :src="`${AMAP_API_URL}${location}&zoom=10&size=400*200&scale=1&markers=mid,,A:${msgBody.lng},${msgBody.lat}&key=${AMAP_KEY}`"
    />
  </view>

第三步:点击位置消息气泡跳转详细位置地图

js 复制代码
 openMap() {
      uni.openLocation({
        latitude: this.msgBody.lat,
        longitude: this.msgBody.lng,
        success: function () {
          console.log('success');
        },
      });

相关效果展示

选择位置

点击选择位置

位置消息在消息列表的渲染效果

点击消息气泡查看详细位置

遇到的问题

问题一: getLocation失败,不支持gcj02坐标系。

js 复制代码
{
    "errMsg": "getLocation:fail not support gcj02",
    "errCode": 18,
    "code": 18
}

解决方式:进行了重新自定义调试基座。

问题二:地图依赖模块未勾选导致的问题。

解决方式:在mainifest.json勾选地图依赖模块,并重新打包自定义调试基座。

问题三:获取安卓高德 key SHA1 指纹。

为了获取安卓高德 key SHA1 指纹的获取

以 Mac 举例

1. 下载并安装 Java JDK

如果你还没有安装 Java JDK,可以通过以下步骤安装:

  1. 打开 Oracle JDK 下载页面
  2. 下载并安装适合你操作系统的 JDK。
2. 使用 keytool 工具获取 SHA1

keytool 工具是 Java 开发工具包 (JDK) 中的一个命令行工具,用于管理密钥和证书。

假设你的 APK 文件名为 your_app.apk 并且存放在 ~/Downloads 目录中。

  1. 打开终端(Terminal)。
  2. 运行以下命令来提取 APK 包中的证书信息:
sh 复制代码
keytool -printcert -jarfile ~/Downloads/your_app.apk

该命令会显示 APK 包的证书详细信息,包括 SHA1 指纹。

示例输出
ruby 复制代码
  Signature:

  Owner: CN=Your Name, OU=Your Organization, O=Your Company, L=Your City, ST=Your State, C=Your Country
  Issuer: CN=Your Name, OU=Your Organization, O=Your Company, L=Your City, ST=Your State, C=Your Country
  Serial number: 1234567890abcdef
  Valid from: Mon Jul 01 00:00:00 PDT 2021 until: Wed Jun 26 00:00:00 PDT 2041
  Certificate fingerprints:
       SHA1:  AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:00:AA:BB:CC
       SHA256: AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99:00:AA:BB:CC:DD:EE:FF:00:11:22:33:44:55:66:77:88:99
       Signature algorithm name: SHA256withRSA
       Version: 3

从输出中找到 Certificate fingerprints 下的 SHA1 指纹,即为 APK 包的 SHA1 安全码。

注意事项
  1. 确保你使用的 keytool 是 Java JDK 中的工具,默认情况下,keytool 会安装在 /usr/bin 目录中。
  2. 如果你在使用命令时遇到问题,可以尝试指定 keytool 的完整路径,例如 /Library/Java/JavaVirtualMachines/jdk-15.0.1.jdk/Contents/Home/bin/keytool,具体路径根据你安装的 JDK 版本而定。

通过上述步骤,你可以在 Mac 上获取 UniApp 云打包的 APK 包的 SHA1 安全码。

问题四:uni.chooseLocation无法定位的问题。

该问题在安卓以及 iOS 均有发现,触发时机是在调用选择地图时高频复现,目前暂不确定是否是uni.chooseLocationapi 的异步调用而产生的 bug,在产生时,选择位置界面无法展示设备所在位置。

解决方案:

经测试该方案使该情况有所好转,但是偶尔还是能复现无法定位的情况,如果你知道原因以及解决方案可以评论区留言。

调用uni.chooseLocation时将通过uni.getLocation经纬度坐标一并传入,代码如下:

js 复制代码
    chooseLocationData() {
      uni.chooseLocation({
      //获取到的经纬度
        latitude: this.location.latitude,
        longitude: this.location.longitude,
        success: (res) => {
          console.log('位置名称:' + res.name);
          console.log('详细地址:' + res.address);
          console.log('纬度:' + res.latitude);
          console.log('经度:' + res.longitude);
          this.location = Object.assign(this.location, { ...res });
        },
        fail: (err) => {
          console.error(err);
        },
      });
    },

相关文档:

相关推荐
JH307313 分钟前
Oracle与MySQL中CONCAT()函数的使用差异
数据库·mysql·oracle
蓝染-惣右介15 分钟前
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
java·数据库·tomcat·mybatis
冷心笑看丽美人16 分钟前
Spring框架特性及包下载(Java EE 学习笔记04)
数据库
武子康1 小时前
Java-07 深入浅出 MyBatis - 一对多模型 SqlMapConfig 与 Mapper 详细讲解测试
java·开发语言·数据库·sql·mybatis·springboot
代码吐槽菌2 小时前
基于SSM的毕业论文管理系统【附源码】
java·开发语言·数据库·后端·ssm
路有瑶台2 小时前
MySQL数据库学习(持续更新ing)
数据库·学习·mysql
数字扫地僧2 小时前
WebLogic 版本升级的注意事项与流程
数据库
Viktor_Ye2 小时前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
努力算法的小明3 小时前
SQL 复杂查询
数据库·sql
斗-匕3 小时前
MySQL 三大日志详解
数据库·mysql·oracle