通过实现标记聚类,即使在地图上放置大量标记,也不会影响用户浏览地图的体验。
简介
这段视频介绍了当数据较多,需要在地图上显示大量数据点时,如何利用标记聚类技术来改善浏览体验。
标记聚类实用程序可帮助您在不同的缩放级别管理多个标记。准确来说,此时的“标记”实际上只是“对象”,只有在渲染时,这些“对象”才会成为“标记”。但是为清楚起见,本文全篇均将其称作“标记”。
当用户以较高的放大倍数查看地图时,地图上会显示每一个具体标记。当用户缩小地图时,这些标记会聚拢形成聚类,以方便用户查看地图。标记聚类实用程序包含在 Maps SDK for Android 实用程序库中。如果您尚未设置该库,请先按照设置指南操作,然后再阅读本页面的其余内容。
![一幅包含聚类标记的地图](https://developers.google.cn/static/maps/documentation/android-sdk/images/utility-markercluster.png?authuser=8&hl=sr)
如需使用标记聚类实用程序,您需要将标记作为 ClusterItem
对象添加到 ClusterManager
。ClusterManager
会将标记传递给 Algorithm
,后者则会将标记转变成一组聚类。ClusterRenderer
负责添加和移除聚类和具体标记,即渲染过程。ClusterRenderer
和 Algorithm
是可以插入的,并可进行自定义。
实用程序库自带一个演示版应用,其中包含标记聚类实用程序的实现代码示例。如果在运行该演示版应用方面需要帮助,请参阅设置指南。该演示版应用包含下列标记聚类示例:
ClusteringDemoActivity
:一个演示标记聚类的简单 activity。BigClusteringDemoActivity
:包含 2000 个标记的聚类。CustomMarkerClusteringDemoActivity
:为已聚类的标记创建自定义设计。
添加简单的标记聚类器
按照以下步骤创建一个包含十个标记的简单聚类。渲染结果大致如下图所示,不过显示/聚类的标记数量将随缩放级别而发生变化:
![一幅包含十个已聚类标记的地图](https://developers.google.cn/static/maps/documentation/android-sdk/images/utility-markercluster-simple.png?authuser=8&hl=sr)
下面概要列出了所需步骤:
- 实现
ClusterItem
以表示地图上的标记。此聚类项以一个 LatLng 对象的形式返回标记的位置,并可返回一个可选的标题或信息摘要。 - 新增一个
ClusterManager
,以根据缩放级别对聚类项(标记)进行分组。 - 将地图的
OnCameraIdleListener()
设置为ClusterManager
,因为ClusterManager
会实现监听器。 - 如果您想添加特定功能来响应标记点击事件,请将地图的
OnMarkerClickListener()
设置为ClusterManager
,因为ClusterManager
会实现监听器。 - 将标记送入
ClusterManager
。
以下列出了更详细的步骤:如需创建包含十个标记的简单聚类,请先创建实现 ClusterItem
的 MyItem
类。
Kotlin
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 } }
Java
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
类型。
Kotlin
// 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) } }
Java
// 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()
,如下所示:
Kotlin
clusterManager.setAnimation(false)
Java
clusterManager.setAnimation(false);
为已聚类的具体标记添加信息窗口
要为已聚类的特定标记添加信息窗口,请在 ClusterItem
实现代码的构造函数中添加标题和信息摘要字符串。
下例使用 addItems()
方法,通过设置标题和信息摘要添加了一个带有信息窗口的标记:
Kotlin
// 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)
Java
// 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
。
![一幅包含自定义的已聚类标记的地图](https://developers.google.cn/static/maps/documentation/android-sdk/images/utility-markercluster-custom.png?authuser=8&hl=sr)
CustomMarkerClusteringDemoActivity
定义了自己的聚类项 Person
,并通过将 DefaultClusterRenderer
扩展为 PersonRenderer
来渲染该聚类项。
该演示版应用还展示了如何实现 ClusterManager.OnClusterClickListener<Person>
接口,以在用户点击聚类时显示相关人员的详细信息。您还可以通过类似方式实现 ClusterManager.OnClusterItemClickListener<Person>
。
如果在运行该演示版应用方面需要帮助,请参阅设置指南。