Google Maps Android 标记聚类实用程序

请选择平台: Android iOS JavaScript

通过实现标记聚类,即使在地图上放置大量标记,也不会影响用户浏览地图的体验。

简介

这段视频介绍了当数据较多,需要在地图上显示大量数据点时,如何利用标记聚类技术来改善浏览体验。

标记聚类实用程序可帮助您在不同的缩放级别管理多个标记。准确来说,此时的“标记”实际上只是“对象”,只有在渲染时,这些“对象”才会成为“标记”。但是为清楚起见,本文全篇均将其称作“标记”。

当用户以较高的放大倍数查看地图时,地图上会显示每一个具体标记。当用户缩小地图时,这些标记会聚拢形成聚类,以方便用户查看地图。标记聚类实用程序包含在 Maps SDK for Android 实用程序库中。如果您尚未设置该库,请先按照设置指南操作,然后再阅读本页面的其余内容。

一幅包含聚类标记的地图
已聚类的标记

如需使用标记聚类实用程序,您需要将标记作为 ClusterItem 对象添加到 ClusterManagerClusterManager 会将标记传递给 Algorithm,后者则会将标记转变成一组聚类。ClusterRenderer 负责添加和移除聚类和具体标记,即渲染过程。ClusterRendererAlgorithm 是可以插入的,并可进行自定义。

实用程序库自带一个演示版应用,其中包含标记聚类实用程序的实现代码示例。如果在运行该演示版应用方面需要帮助,请参阅设置指南。该演示版应用包含下列标记聚类示例:

  • ClusteringDemoActivity:一个演示标记聚类的简单 activity。
  • BigClusteringDemoActivity:包含 2000 个标记的聚类。
  • CustomMarkerClusteringDemoActivity:为已聚类的标记创建自定义设计。

添加简单的标记聚类器

按照以下步骤创建一个包含十个标记的简单聚类。渲染结果大致如下图所示,不过显示/聚类的标记数量将随缩放级别而发生变化:

一幅包含十个已聚类标记的地图
十个已聚类的标记

下面概要列出了所需步骤:

  1. 实现 ClusterItem 以表示地图上的标记。此聚类项以一个 LatLng 对象的形式返回标记的位置,并可返回一个可选的标题或信息摘要。
  2. 新增一个 ClusterManager,以根据缩放级别对聚类项(标记)进行分组。
  3. 将地图的 OnCameraIdleListener() 设置为 ClusterManager,因为 ClusterManager 会实现监听器。
  4. 如果您想添加特定功能来响应标记点击事件,请将地图的 OnMarkerClickListener() 设置为 ClusterManager,因为 ClusterManager 会实现监听器。
  5. 将标记送入 ClusterManager

以下列出了更详细的步骤:如需创建包含十个标记的简单聚类,请先创建实现 ClusterItemMyItem 类。

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。您可以使用 ClusterManagersetAlgorithm(Algorithm<T> algorithm)setRenderer(ClusterRenderer<T> view) 方法更改 ClusterRendererAlgorithm

您可以通过实现 ClusterRenderer 来自定义聚类的渲染方式。DefaultClusterRenderer 提供了一个很好的基础作为起始点。您可以通过为 DefaultClusterRenderer 创建子类来覆盖默认值。

如需查看深入的自定义示例,请参考实用程序库附带的演示版应用中的 CustomMarkerClusteringDemoActivity

一幅包含自定义的已聚类标记的地图
自定义的已聚类标记

CustomMarkerClusteringDemoActivity 定义了自己的聚类项 Person,并通过将 DefaultClusterRenderer 扩展为 PersonRenderer 来渲染该聚类项。

该演示版应用还展示了如何实现 ClusterManager.OnClusterClickListener<Person> 接口,以在用户点击聚类时显示相关人员的详细信息。您还可以通过类似方式实现 ClusterManager.OnClusterItemClickListener<Person>

如果在运行该演示版应用方面需要帮助,请参阅设置指南