【问题标题】:Android how to uncluster on single tap on a cluster marker maps v2Android 如何在集群标记图 v2 上单击时取消集群
【发布时间】:2020-09-30 04:34:43
【问题描述】:

我正在使用 Google Maps Marker Clustering Utility 对标记进行聚类。它在双击时取消聚类。是否可以单击手动完成。

【问题讨论】:

    标签: android google-maps


    【解决方案1】:

    聚会迟到了

    在你的 onClusterClick 函数中:

    LatLngBounds.Builder builder = LatLngBounds.builder();
    for (ClusterItem item : cluster.getItems()) {
        builder.include(item.getPosition());
    }
    final LatLngBounds bounds = builder.build();
    getMap().animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100));
    

    【讨论】:

      【解决方案2】:
          mClusterManager
                  .setOnClusterClickListener(new OnClusterClickListener<MyItem>() {
                      @Override
                      public boolean onClusterClick(final Cluster<MyItem> cluster) {
                          map.animateCamera(CameraUpdateFactory.newLatLngZoom(
                                  cluster.getPosition(), (float) Math.floor(map
                                          .getCameraPosition().zoom + 1)), 300,
                                  null);
                          return true;
                      }
                  });
      

      你也可以通过点击标记来做到这一点, 但在此之前你需要做map.setOnMarkerClickListener(mClusterManager);

      以便集群管理器获取点击事件并 你可以做

      mClusterManagersetOnClusterItemClickListener(new OnClusterItemClickListener<MyItem>() {
      
      }
      

      【讨论】:

      • 非常感谢。那行得通!没想到获取当前缩放并增加它。
      • 但这是否会“取消”集群中的“所有”项目?当有这么多集群项目时,我不认为将缩放增加 1 会做到这一点。
      • 那没有回答问题,你可以用正确的方式增加缩放:CameraUpdateFactory.zoomIn()CameraUpdateFactory.zoomTo()CameraUpdateFactory.zoomBy()
      【解决方案3】:

      对于想要扩展集群并保持缩放级别的用户,请尝试以下步骤。看起来很复杂,其实不然。

      步骤

      1. 假设您有一个名为 Location 的模型实现了 ClusterItem,添加以下代码:

        private boolean shouldCluster = true;
        
        public boolean isShouldCluster() {
            return shouldCluster;
        }
        
        public void setShouldCluster(boolean shouldCluster) {
            this.shouldCluster = shouldCluster;
        }
        

        整个模型看起来像这样:

        public class Location implements ClusterItem
        {
            private double latitude;
        
            private double longitude;
        
            private boolean shouldCluster = true;
        
            @Override
            public LatLng getPosition() {
                return new LatLng(latitude, longitude);
            }
        
            @Override
            public String getTitle() {
                return null;
            }
        
            @Override
            public String getSnippet() {
                return null;
            }
        
            public boolean isShouldCluster() {
                return shouldCluster;
            }
        
            public void setShouldCluster(boolean shouldCluster) {
                this.shouldCluster = shouldCluster;
            }
        }
        
      2. 将以下算法添加到您的项目中,并将 Location 替换为 您自己的模型名称。注意:此算法只是 Google 的 NonHierarchicalDistanceBasedAlgorithm 的副本,并进行了一些修改。

        public class DistanceBasedAlgorithm implements Algorithm<Location> {
            public static final int MAX_DISTANCE_AT_ZOOM = 100; // essentially 100 dp.
        
            /**
             * Any modifications should be synchronized on mQuadTree.
             */
            private final Collection<QuadItem> mItems = new ArrayList<QuadItem>();
        
            /**
             * Any modifications should be synchronized on mQuadTree.
             */
            private final PointQuadTree<QuadItem> mQuadTree = new PointQuadTree<QuadItem>(0, 1, 0, 1);
        
            private static final SphericalMercatorProjection PROJECTION = new SphericalMercatorProjection(1);
        
            @Override
            public void addItem(Location item) {
                if (item == null) return;
        
                final QuadItem quadItem = new QuadItem(item);
                synchronized (mQuadTree) {
                    mItems.add(quadItem);
                    mQuadTree.add(quadItem);
                }
            }
        
            @Override
            public void addItems(Collection<Location> items) {
                for (Location item : items) {
                    addItem(item);
                }
            }
        
            @Override
            public void clearItems() {
                synchronized (mQuadTree) {
                    mItems.clear();
                    mQuadTree.clear();
                }
            }
        
            @Override
            public void removeItem(Location item) {
                // QuadItem delegates hashcode() and equals() to its item so,
                //   removing any QuadItem to that item will remove the item
                final QuadItem quadItem = new QuadItem(item);
                synchronized (mQuadTree) {
                    mItems.remove(quadItem);
                    mQuadTree.remove(quadItem);
                }
            }
        
            @Override
            public Set<? extends Cluster<Location>> getClusters(double zoom) {
                final int discreteZoom = (int) zoom;
        
                final double zoomSpecificSpan = MAX_DISTANCE_AT_ZOOM / Math.pow(2, discreteZoom) / 256;
        
                final Set<QuadItem> visitedCandidates = new HashSet<QuadItem>();
                final Set<Cluster<Location>> results = new HashSet<Cluster<Location>>();
                final Map<QuadItem, Double> distanceToCluster = new HashMap<QuadItem, Double>();
                final Map<QuadItem, StaticCluster<Location>> itemToCluster = new HashMap<QuadItem, StaticCluster<Location>>();
        
                synchronized (mQuadTree) {
                    for (QuadItem candidate : mItems) {
                        if (visitedCandidates.contains(candidate)) {
                            // Candidate is already part of another cluster.
                            continue;
                        }
        
                        Collection<QuadItem> clusterItems;
        
                        if (candidate.mClusterItem.isShouldCluster()) {
                            Bounds searchBounds = createBoundsFromSpan(candidate.getPoint(), zoomSpecificSpan);
                            clusterItems = mQuadTree.search(searchBounds);
                        }
                        else {
                            List<QuadItem> temp = new ArrayList<>();
                            temp.add(candidate);
                            clusterItems = temp;
                        }
        
                        if (clusterItems.size() == 1) {
                            // Only the current marker is in range. Just add the single item to the results.
                            results.add(candidate);
                            visitedCandidates.add(candidate);
                            distanceToCluster.put(candidate, 0d);
                            continue;
                        }
                        StaticCluster<Location> cluster = new StaticCluster<Location>(candidate.mClusterItem.getPosition());
                        results.add(cluster);
        
                        for (QuadItem clusterItem : clusterItems) {
                            Double existingDistance = distanceToCluster.get(clusterItem);
                            double distance = distanceSquared(clusterItem.getPoint(), candidate.getPoint());
                            if (existingDistance != null) {
                                // Item already belongs to another cluster. Check if it's closer to this cluster.
                                if (existingDistance < distance) {
                                    continue;
                                }
                                // Move item to the closer cluster.
                                itemToCluster.get(clusterItem).remove(clusterItem.mClusterItem);
                            }
                            distanceToCluster.put(clusterItem, distance);
                            cluster.add(clusterItem.mClusterItem);
                            itemToCluster.put(clusterItem, cluster);
                        }
                        visitedCandidates.addAll(clusterItems);
                    }
                }
                return results;
            }
        
            @Override
            public Collection<Location> getItems() {
                final List<Location> items = new ArrayList<Location>();
                synchronized (mQuadTree) {
                    for (QuadItem quadItem : mItems) {
                        items.add(quadItem.mClusterItem);
                    }
                }
                return items;
            }
        
            private double distanceSquared(Point a, Point b) {
                return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
            }
        
            private Bounds createBoundsFromSpan(Point p, double span) {
                // TODO: Use a span that takes into account the visual size of the marker, not just its
                // LatLng.
                double halfSpan = span / 2;
                return new Bounds(
                        p.x - halfSpan, p.x + halfSpan,
                        p.y - halfSpan, p.y + halfSpan);
            }
        
            private static class QuadItem implements PointQuadTree.Item, Cluster<Location> {
                private final Location mClusterItem;
                private final Point mPoint;
                private final LatLng mPosition;
                private Set<Location> singletonSet;
        
                private QuadItem(Location item) {
                    mClusterItem = item;
                    mPosition = item.getPosition();
                    mPoint = PROJECTION.toPoint(mPosition);
                    singletonSet = Collections.singleton(mClusterItem);
                }
        
                @Override
                public Point getPoint() {
                    return mPoint;
                }
        
                @Override
                public LatLng getPosition() {
                    return mPosition;
                }
        
                @Override
                public Set<Location> getItems() {
                    return singletonSet;
                }
        
                @Override
                public int getSize() {
                    return 1;
                }
        
                @Override
                public int hashCode() {
                    return mClusterItem.hashCode();
                }
        
                @Override
                public boolean equals(Object other) {
                    if (!(other instanceof QuadItem)) {
                        return false;
                    }
        
                    return ((QuadItem) other).mClusterItem.equals(mClusterItem);
                }
            }
        }
        
      3. 设置算法和集群点击监听

        map.setOnMarkerClickListener(mClusterManager);
        mClusterManager.setAlgorithm(new DistanceBasedAlgorithm());
        mClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener<Location>() {
                    @Override
                    public boolean onClusterClick(Cluster<Location> cluster) {
                        for (Location location : cluster.getItems()) {
                            location.setShouldCluster(false);
                        }
                        mClusterManager.addItem(null); // this line resets the cache
                        mClusterManager.cluster(); // re-cluster
        
                        return false;
                    }
                });
        
      4. 完成!

      说明

      对DistanceBasedAlgorithm如何启用去集群的一点解释

      1. getClusters函数中,下面的sn-p检查item是否应该是cluster

                if (candidate.mClusterItem.isShouldCluster()) {
                    Bounds searchBounds = createBoundsFromSpan(candidate.getPoint(), zoomSpecificSpan);
                    clusterItems = mQuadTree.search(searchBounds);
                }
                else {
                    List<QuadItem> temp = new ArrayList<>();
                    temp.add(candidate);
                    clusterItems = temp;
                }
        
      2. addItem函数的变化,允许算法接受一个空值,从而允许PreCachingAlgorithmDecorator清除缓存。

        @Override
        public void addItem(Location item) {
            if (item == null) return; // this line is the key to reset cache
        
            final QuadItem quadItem = new QuadItem(item);
            synchronized (mQuadTree) {
                mItems.add(quadItem);
                mQuadTree.add(quadItem);
            }
        }
        

      我已经有一段时间没有写代码了。我可能会错过一些东西。如果您无法使此解决方案发挥作用,请给我留言。谢谢!

      【讨论】:

      • 如何重新启用集群?您在单击集群后设置了ShouldCluster(false),但是如何在地图滚动或任何其他操作中启用它?
      • @NickUnuchek 已经有一段时间了。我不记得细节了。我相信 DistanceBasedAlgorithm.getClusters 会在应用操作时重新创建所有项目,默认 shouldCluster 为 true。在我们的产品中,重新集群没有任何问题。
      猜你喜欢
      • 2013-03-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-12-29
      • 2014-11-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多