最近的项目需求中需要做聚合功能,研究了一下官方demo,发现官方Demo有以下两个用起来不太方便的点:
- 需要修改ClusterOverlay才能实现自己的Marker绘制逻辑。(仅聚合簇的绘制开放了接口)。
- 不能批量的动态添加和移除数据,如果要做这个功能的话,还是要修改官方的ClusterOverlay实现。
为了解决这两个问题,自己实现了一个聚合工具类。主要逻辑和官方demo的逻辑差不多。
大概的聚合逻辑如下:
- 定义聚合簇的结构,它由锚点和吸附于它的一系列点组成,锚点本身也对应着一个有具体数据的标记物。锚点一定范围内的点被吸附到这个聚合簇。
- 如果当前不存在任何聚合簇,则被循环到的第一个点作为第一个聚合簇的锚点。
- 如果已经存在聚合簇,则对于其他点,在需要聚合的缩放级别下,判断它是否位于聚合簇锚点的范围阈值(单位为m)内。这个范围阈值等于clusterPXSize*map.scalePerPixel,如果它位于这个范围,则其依附于该聚合簇。
- 对于聚合完成后,依然没有依附于任何聚合簇的孤立点,不将其绘制为聚合簇的标记物形式,而是直接绘制为未聚合状态下的标记物形式。
这个逻辑还是挺简单的,官方demo也是这样实现的。除此之外,官方demo加入了聚合物的BitmapDescriptor缓存和开线程计算等优化步骤。
我自己写的就没有单开线程进行计算了。
实现效果如下:
如上图,图片中的蓝色标记物是无法被聚合的孤立点,黑色标记物是聚合簇。
代码如下:
@file:Suppress("unused")
package com.bian.cluster
import android.content.Context
import android.graphics.Color
import android.util.LruCache
import android.util.TypedValue
import android.view.Gravity
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.widget.TextView
import com.amap.api.maps.AMap
import com.amap.api.maps.AMapUtils.calculateLineDistance
import com.amap.api.maps.CameraUpdateFactory
import com.amap.api.maps.model.*
import com.amap.api.maps.model.animation.AlphaAnimation
import com.amap.api.maps.model.animation.Animation
import com.bian.cluster.CustomClusterOverlay.ClusterModel
/**
* author fhbianling
* date 2020/6/11 17:26
* 类描述:高德聚合工具类
* 聚合逻辑:
* 1.如果当前不存在任何聚合锚点,则被循环到的第一个点作为聚合锚点
* 2.如果已经存在聚合簇,则对于其他点,在任意map的zoom情况下,判断它是否位于聚合簇锚点的范围阈值(单位为m)内。
* 这个范围阈值等于clusterPXSize*map.scalePerPixel,如果它位于这个范围,则其依附于该聚合簇
* 3.对于聚合完成后,依然没有依附于任何聚合簇的孤立点,不将其绘制为聚合簇的标记物形式,而是直接绘制为未聚合状态下的标记物形式
*
* [ClusterModel]聚合锚点
* [CustomClusterOverlay.clusterRender] 聚合簇标记物渲染器
* [CustomClusterOverlay.markerRender] 未聚合状态下标记物的渲染器
* [CustomClusterOverlay.clusterPXSize]聚合判断的屏幕像素范围
* [CustomClusterOverlay.clusterDisappearZoom]当map.zoom大于或等于该值时,不再进行聚合
*/
class CustomClusterOverlay<T : CustomClusterOverlay.Model>(
private val map: AMap,
private val clusterPXSize: Int,
private val context: Context,
private val zMarkerIndex: Float,
private val zClusterIndex: Float,
private var markerRender: MarkerRender<T>? = null,
private var clusterRender: ClusterRender? = null,
private var clusterDisappearZoom: Int = CLUSTER_DISAPPEAR_ZOOM
) {
private val data = mutableListOf<T>()
private val showingMarkers = mutableMapOf<T, Marker>()
private val clusterMarkers = mutableMapOf<ClusterModel<T>, Marker>()
private val defaultMarkerRender by lazy { DefaultMarkerRender<T>() }
private val defaultClusterRender by laz