通过实现标记聚类,即使在地图上放置大量标记,也不会影响用户浏览地图的体验。
简介
这段视频介绍了当数据较多,需要在地图上显示大量数据点时,如何利用标记聚类技术来改善浏览体验。
标记聚类实用程序可帮助您在不同的缩放级别管理多个标记。准确来说,此时的“标记”实际上只是“对象”,只有在渲染时,这些“对象”才会成为“标记”。但是为清楚起见,本文全篇均将其称作“标记”。
当用户以较高的放大倍数查看地图时,地图上会显示每一个具体标记。当用户缩小地图时,这些标记会聚拢形成聚类,以方便用户查看地图。标记聚类实用程序包含在 Maps SDK for Android 实用程序库中。如果您尚未设置该库,请先按照设置指南操作,然后再阅读本页面的其余内容。

如需使用标记聚类实用程序,您需要将标记作为 ClusterItem
对象添加到 ClusterManager
。ClusterManager
会将标记传递给 Algorithm
,后者则会将标记转变成一组聚类。ClusterRenderer
负责添加和移除聚类和具体标记,即渲染过程。ClusterRenderer
和 Algorithm
是可以插入的,并可进行自定义。
实用程序库自带一个演示版应用,其中包含标记聚类实用程序的实现代码示例。如果在运行该演示版应用方面需要帮助,请参阅设置指南。该演示版应用包含下列标记聚类示例:
ClusteringDemoActivity
:一个演示标记聚类的简单 activity。BigClusteringDemoActivity
:包含 2000 个标记的聚类。CustomMarkerClusteringDemoActivity
:为已聚类的标记创建自定义设计。
添加简单的标记聚类器
按照以下步骤创建一个包含十个标记的简单聚类。渲染结果大致如下图所示,不过显示/聚类的标记数量将随缩放级别而发生变化:

下面概要列出了所需步骤:
- 实现
ClusterItem
以表示地图上的标记。此聚类项以一个 LatLng 对象的形式返回标记的位置,并可返回一个可选的标题或信息摘要。 - 新增一个
ClusterManager
,以根据缩放级别对聚类项(标记)进行分组。 - 将地图的
OnCameraIdleListener()
设置为ClusterManager
,因为ClusterManager
会实现监听器。 - 如果您想添加特定功能来响应标记点击事件,请将地图的
OnMarkerClickListener()
设置为ClusterManager
,因为ClusterManager
会实现监听器。 - 将标记送入
ClusterManager
。
以下列出了更详细的步骤:如需创建包含十个标记的简单聚类,请先创建实现 ClusterItem
的 MyItem
类。
inner class MyItem(
lat: Double,
lng: Double,
title: String,
snippet: String
) : ClusterItem {
private val position: LatLng
private val title: String
private val snippet: String
override fun getPosition(): LatLng {
return position
}
override fun getTitle(): String {
return title
}
override fun getSnippet(): String {
return snippet
}
override fun getZIndex(): Float {
return 0f
}
init {
position = LatLng(lat, lng)
this.title = title
this.snippet = snippet
}
}
public class MyItem implements ClusterItem {
private final LatLng position;
private final String title;
private final String snippet;
public MyItem(double lat, double lng, String title, String snippet) {
position = new LatLng(lat, lng);
this.title = title;
this.snippet = snippet;
}
@Override
public LatLng getPosition() {
return position;
}
@Override
public String getTitle() {
return title;
}
@Override
public String getSnippet() {
return snippet;
}
@Nullable
@Override
public Float getZIndex() {
return 0f;
}
}
在您的地图 activity 中,添加 ClusterManager
并向其提供聚类项。请注意类型参数 <MyItem>
会将 ClusterManager
声明为 MyItem
类型。
// Declare a variable for the cluster manager.
private lateinit var clusterManager: ClusterManager<MyItem>
private fun setUpClusterer() {
// Position the map.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(51.503186, -0.126446), 10f))
// Initialize the manager with the context and the map.
// (Activity extends context, so we can pass 'this' in the constructor.)
clusterManager = ClusterManager(context, map)
// Point the map's listeners at the listeners implemented by the cluster
// manager.
map.setOnCameraIdleListener(clusterManager)
map.setOnMarkerClickListener(clusterManager)
// Add cluster items (markers) to the cluster manager.
addItems()
}
private fun addItems() {
// Set some lat/lng coordinates to start with.
var lat = 51.5145160
var lng = -0.1270060
// Add ten cluster items in close proximity, for purposes of this example.
for (i in 0..9) {
val offset = i / 60.0
lat += offset
lng += offset
val offsetItem =
MyItem(lat, lng, "Title $i", "Snippet $i")
clusterManager.addItem(offsetItem)
}
}
// Declare a variable for the cluster manager.
private ClusterManager<MyItem> clusterManager;
private void setUpClusterer() {
// Position the map.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.503186, -0.126446), 10));
// Initialize the manager with the context and the map.
// (Activity extends context, so we can pass 'this' in the constructor.)
clusterManager = new ClusterManager<MyItem>(context, map);
// Point the map's listeners at the listeners implemented by the cluster
// manager.
map.setOnCameraIdleListener(clusterManager);
map.setOnMarkerClickListener(clusterManager);
// Add cluster items (markers) to the cluster manager.
addItems();
}
private void addItems() {
// Set some lat/lng coordinates to start with.
double lat = 51.5145160;
double lng = -0.1270060;
// Add ten cluster items in close proximity, for purposes of this example.
for (int i = 0; i < 10; i++) {
double offset = i / 60d;
lat = lat + offset;
lng = lng + offset;
MyItem offsetItem = new MyItem(lat, lng, "Title " + i, "Snippet " + i);
clusterManager.addItem(offsetItem);
}
}
您还可以选择在缩放时停用聚类动画。如果已关闭动画,则标记会直接跳至对应位置,而不会显示移出和移入聚类的过程。
如需停用动画,请使用 ClusterManager
中的 setAnimation()
,如下所示:
clusterManager.setAnimation(false)
clusterManager.setAnimation(false);
为已聚类的具体标记添加信息窗口
要为已聚类的特定标记添加信息窗口,请在 ClusterItem
实现代码的构造函数中添加标题和信息摘要字符串。
下例使用 addItems()
方法,通过设置标题和信息摘要添加了一个带有信息窗口的标记:
// Set the lat/long coordinates for the marker.
val lat = 51.5009
val lng = -0.122
// Set the title and snippet strings.
val title = "This is the title"
val snippet = "and this is the snippet."
// Create a cluster item for the marker and set the title and snippet using the constructor.
val infoWindowItem = MyItem(lat, lng, title, snippet)
// Add the cluster item (marker) to the cluster manager.
clusterManager.addItem(infoWindowItem)
// Set the lat/long coordinates for the marker.
double lat = 51.5009;
double lng = -0.122;
// Set the title and snippet strings.
String title = "This is the title";
String snippet = "and this is the snippet.";
// Create a cluster item for the marker and set the title and snippet using the constructor.
MyItem infoWindowItem = new MyItem(lat, lng, title, snippet);
// Add the cluster item (marker) to the cluster manager.
clusterManager.addItem(infoWindowItem);
自定义标记聚类
ClusterManager
构造函数会创建一个 DefaultClusterRenderer
和一个 NonHierarchicalDistanceBasedAlgorithm
。您可以使用 ClusterManager
的 setAlgorithm(Algorithm<T> algorithm)
和 setRenderer(ClusterRenderer<T> view)
方法更改 ClusterRenderer
和 Algorithm
。
您可以通过实现 ClusterRenderer
来自定义聚类的渲染方式。DefaultClusterRenderer
提供了一个很好的基础作为起始点。您可以通过为 DefaultClusterRenderer
创建子类来覆盖默认值。
如需查看深入的自定义示例,请参考实用程序库附带的演示版应用中的 CustomMarkerClusteringDemoActivity
。

CustomMarkerClusteringDemoActivity
定义了自己的聚类项 Person
,并通过将 DefaultClusterRenderer
扩展为 PersonRenderer
来渲染该聚类项。
该演示版应用还展示了如何实现 ClusterManager.OnClusterClickListener<Person>
接口,以在用户点击聚类时显示相关人员的详细信息。您还可以通过类似方式实现 ClusterManager.OnClusterItemClickListener<Person>
。
如果在运行该演示版应用方面需要帮助,请参阅设置指南。