【问题标题】:Best way to render only the visible cluster items on a google map仅在谷歌地图上呈现可见集群项目的最佳方式
【发布时间】:2016-12-05 01:54:54
【问题描述】:

我有一个由谷歌集群管理器处理的地图片段,我使用我的自定义集群渲染器 extends DefaultClusterRenderer 进行了配置。

我已经重写了 onBeforeClusterItemRenderedonBeforeClusterRendered 函数以便能够显示我的图片:

现在如果用户放大,渲染不在可见区域中的项目是没有意义的。 很容易找出项目是否在可见区域中:

private Boolean isInBounds(LatLng position) {
    return map.getProjection().getVisibleRegion().latLngBounds.contains(position);
}

但如果我在该项目当前不可见的情况下跳过渲染,则当用户在地图上滚动时它将为空。

那么,谁知道在用户滚动时如何获取事件以及如何重新渲染不在可见范围内的项目? (从可见切换到不可见,反之亦然)?

(对不起,我的英语不好)

【问题讨论】:

  • 您是如何将图像加载到集群标记上的?任何示例代码。谢谢。

标签: android google-maps render markerclusterer


【解决方案1】:

这是我的解决方案。它工作得很好,只渲染可见项目。 我使用相机更改的侦听器重新渲染现在可见的项目:

private void onBeforeClusterOrClusterItemRendered(final Cluster<MediaItem> cluster, final MediaItem mediaItem, final MarkerOptions markerOption
    if(!isAdded())
        return;

    // In visible area?
    Marker marker = cluster == null ? getMarker(mediaItem) : getMarker(cluster);
    Boolean isInBounds = isInBounds(marker != null ? marker.getPosition() : mediaItem.getPosition(), null);

    if(isInBounds) {
        // ...
    }
}

private Boolean isInBounds(LatLng position, LatLngBounds latLngBounds) {
    return (latLngBounds == null ? mMap.getProjection().getVisibleRegion().latLngBounds : latLngBounds).contains(position);
}

@Override
protected void onBeforeClusterItemRendered(final MediaItem mediaItem, final MarkerOptions markerOptions) {
    onBeforeClusterOrClusterItemRendered(null, mediaItem, markerOptions);
}

@Override
protected void onBeforeClusterRendered(final Cluster<MediaItem> cluster, final MarkerOptions markerOptions) {
    final MediaItem mediaItem = MediaPicker.getBestRated(new ArrayList<>(cluster.getItems()));
    onBeforeClusterOrClusterItemRendered(cluster, mediaItem, markerOptions);
}

...

// Re render
mMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
    @Override
    public void onCameraChange(CameraPosition cameraPosition) {
        mClusterManager.onCameraChange(cameraPosition);
        final LatLngBounds latLngBounds = mMap.getProjection().getVisibleRegion().latLngBounds;

        // Cluster only
        Collection<Marker> clusters = mClusterManager.getClusterMarkerCollection().getMarkers();
        for(Marker marker : clusters) {
            if(isInBounds(marker.getPosition(), latLngBounds))
                onBeforeClusterRendered(getCluster(marker), new MarkerOptions());
        }

        // Cluster item only
        Collection<Marker> markers = mClusterManager.getMarkerCollection().getMarkers();
        for(Marker marker : markers) {
            if(isInBounds(marker.getPosition(), latLngBounds))
                onBeforeClusterItemRendered(getClusterItem(marker), new MarkerOptions());
        }
    }
});

【讨论】:

  • isAdded() 方法丢失
  • 如果需要,您可以跳过此行。它是一种 Fragment 的方法。
  • 经过几次测试后,我将代码更改为仅在地图上添加可见项目。这使它更加平滑。
  • 调用 mClusterManager.onCameraChange(cameraPosition);给了我一个 stackoverflow 异常。
  • 嗨,有人可以澄清一下在if(isInBounds) { // ... }里面放什么@
【解决方案2】:

我有类似的问题。我跟踪用户的初始位置并在他周围显示地图标记。如果用户选择在初始缩放之外查看,我会下载其他标记。实现:

map.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {

            @Override
            public void onCameraChange(CameraPosition cameraPosition) {
                setCurrentRadius();
                viewCenter = cameraPosition.target;
                if (isNewGrounds(viewCenter, viewRadius)) {
                    downloadExtraPrinters(viewCenter,viewRadius);
                }
                myClusterManager.onCameraChange(cameraPosition);
            }
        });

   private void setCurrentRadius() {
        Projection projection = map.getProjection();
        VisibleRegion currentView = projection.getVisibleRegion();
        LatLng cameraCenter = map.getCameraPosition().target;
        float[] projectionRadius = new float[1];
        Location.distanceBetween(currentView.farLeft.latitude, currentView.farLeft.longitude, cameraCenter.latitude, cameraCenter.longitude, projectionRadius);
        viewRadius = Math.round((projectionRadius[0] / 1000.0f) + 0.5f);
    }

 /** Method checks if camera goes outside bounds of previously downloaded area **/
    protected boolean isNewGrounds(LatLng center, int radius) {
        //Check if it is the first time to update
        if (coveredGroundsCenter== null) {

            coveredGroundsCenter=center;
            coveredGroundsRadius= (int)(radius * EXTRA_RADIUS);

            return true;
        }else {
            float[] centerDistance = new float[1];
            Location.distanceBetween(coveredGroundsCenter.latitude, coveredGroundsCenter.longitude, center.latitude, center.longitude, centerDistance);

            int criticalDistance = (int) Math.round((centerDistance[0] / 1000.0f) + 0.5f) + radius;

            if (coveredGroundsRadius >= criticalDistance) {
                return false;
            } else {
                coveredGroundsCenter = center;
                coveredGroundsRadius = (int) (radius * EXTRA_RADIUS);
                return true;
            }
        }
    }

EXTRA_RADIUS 是我用来在可见地图区域之外留出小边距的常量,因此最小的相机移动不会导致我的地图下载和重新渲染。我选择这个边距为当前半径的 50%:

 private static final double EXTRA_RADIUS=1.5;
 private LatLng coveredGroundsCenter;
 private int coveredGroundsRadius;
 private LatLng viewCenter;

【讨论】:

  • 谢谢。我想我可以使用你的一部分代码。
  • 您的回答很有用,但在 cameraUpdateListener 中我无法强制重新渲染不可见区域中的项目。如果我调用 clusterManager.clear 和 clusterManager.add... 它可以工作(强制重新渲染所有项目),但这并不干净,所有项目都在闪烁...
  • 嗯。这就是我实现它的方式。我清除然后添加它们。我也使用自定义标记(屏幕上约 400 个)与您一样。不闪不闪。 idk,也许标记的图片质量设置得太高了?如果您发现一些优化,请告诉我,也许我也可以改进我的代码 :)
  • 是的,我认为我有一个很好的解决方案。我会尝试一下,如果我有什么有趣的事情,我会告诉你;)
猜你喜欢
  • 2017-06-13
  • 1970-01-01
  • 1970-01-01
  • 2016-04-03
  • 2013-01-13
  • 1970-01-01
  • 1970-01-01
  • 2013-09-24
  • 1970-01-01
相关资源
最近更新 更多