Android 高德地图轨迹回放

Wafer Li ... 2019-07-30 10:24 Android
  • Android
  • 高德地图
  • 技术开发
  • 踩坑记录
大约 4 分钟

最近公司要求实现一个轨迹回放功能,想着 JS 都有 demo 的功能,Android 实现起来不还是小菜一碟?

结果显然是我太拿衣服了

# 0. 太长不看

全篇都采用高德 API 实现,基于 3dmap 6.9.2 (opens new window)

使用 MovingPointOverlay 实现点的平滑移动

使用 AMap.setCustomRenderer 获取每帧回调,绘制走过的线路

# 1. 需求分析

轨迹回放的功能在高德的 JS Demo (opens new window) 上有,具体来说就是点按照既定路线去移动,同时绘制其走过的路线。

那么我们可以将功能拆解为移动点和绘制线路

本来我想着这种常见的功能在网上随便搜搜就能找到的,然而显然是我太 Naive 了。

搜到的唯二相关的内容,其中一个排版很差,而且估计是公司内部代码,没有给出 demo,第二个则是硬怼一个自定义 View,但是地图肯定是要移动放大之类的,自定义 View 也需要做手势控制,实现难度相对较大。

所以最好还是在高德内部框架上实现这个需求。

# 2. API 调研

经过一番查找之后,我找到了 SmoothMoveMarker 可以实现点的平滑移动功能,然而:

SmoothMoveMarker Deprecated

这里不得不吐槽一下高德的混淆策略,它不仅混淆了代码,同时还把文档混淆了,明明各部分文档都是公开的,将其保留在源代码里面也不会增大多少空间,然而这样全部删掉,导致我不得不切一个网页去看它的 API doc,开发效率急剧下降

在查看了高德地图的 API doc 之后,发现这个类是被 MovingPointOverlay 替代了,也就是说我们需要采用 MovingPointOverlay 实现点的平滑移动

# 3. 获取点位置回调

MovingPointOverlayAPI doc 中,有一个方法叫 setMovingListener,当时的我想当然的认为这个就是点移动时的回调设置方法,因此把画线的逻辑放到了里面去。

原先我将动画的总时间设置为 10 秒,此时点跑得比较快,乍一看没什么问题; 但是当我将动画的时间拉长,点跑得很慢的时候,问题就出现了—— setMovingListener 并不是每帧回调!

也就是说,只有当 Marker 跑到下一个路径点的时候,才能绘制上经过的路线!

这显然会导致线路绘制不平滑,达不到需求的要求。

那怎么办呢?这时候我就进入了一段瞎糊弄的时间,尝试着通过自行实现动画效果来完成需求,然而无论是经纬度计算、屏幕像素计算都存在误差,不太可能在短时间能将需求实现。

正当我想着『万策尽きた!』,无聊浏览高德地图 API doc 的时候,突然发现 AMap 有个 runOnDrawFrame() 方法,是用来触发高德地图的重绘的;

既然有这个方法,那就一定有绘制的回调方法!功夫不负有心人,在往下看了几行之后,我终于发现了这个重要的方法 setCustomRenderer(),而且两者意外的离的很近:

runOnDrawFrame And setCustomRenderer

这个方法接受一个 CustomRenderer 参数,那么什么是 CustomRenderer 呢?

CustomRenderer

从这里看,它有一个 onDrawFrame(),正是绘制每一帧的回调接口,那么我们只要将画线逻辑放到里面去就可以了。

当然,我们也不能抛弃 setMovingListener,因为我们需要它来判断是否已经到达终点。

# 4. 实现逻辑

  1. 使用 MovingPointOverlay 实现点的平滑移动
  2. 使用 AMap.setCustomRenderer 获取每帧回调,绘制经过的线路
  3. 使用 MovingPointOverlay.setMovingListener 判断是否已经到达终点,如果到达终点,则清除经过线路的点

# 5. 坑点

无论是 setCustomRenderer 还是 setMovingListener,它们的回调都是在 OPEN GL 线程回调,而不是在主线程回调

因此,如果需要在动画完毕之后更改 UI,就必须使用 runOnUiThread,否则无论你怎么在里面更改 UI,都不会有任何效果

# 6. Demo

https://github.com/wafer-li/AMapReplayDemo (opens new window)