ClusterManager 重绘 Google maps v2 utils 的标记

Posted

技术标签:

【中文标题】ClusterManager 重绘 Google maps v2 utils 的标记【英文标题】:ClusterManager repaint markers of Google maps v2 utils 【发布时间】:2014-04-12 18:59:24 【问题描述】:

我正在发出服务器请求,当我收到服务器的响应时,我正在 UI 线程上执行 ClusterManager.addItem() 但这些项目没有在地图上绘制,只有当我进行缩放更新时(+, -) 新添加的项目开始出现。我也尝试调试渲染器,但在我更新放大地图之前不会调用 onBeforeClusterRendered / onBeforeClusterItemRendered

任何想法如何刷新地图/clusterManager/markers?

MarkerManager markerManager = new MarkerManager(map);
clusterManager = new ClusterManager<TweetClusterItem>(getActivity(), map, markerManager);
clusterManager.setRenderer(new TweetClusterRenderer(getActivity(), map, clusterManager, defaultMarker));
clusterManager.setOnClusterClickListener(this);
clusterManager.setOnClusterInfoWindowClickListener(this);
clusterManager.setOnClusterItemClickListener(this);
clusterManager.setOnClusterItemInfoWindowClickListener(this);

UiSettings uiSettings = map.getUiSettings();
uiSettings.setZoomControlsEnabled(true);
uiSettings.setMyLocationButtonEnabled(false);

map.setOnCameraChangeListener(clusterManager);
map.setOnMarkerClickListener(clusterManager);
map.setOnInfoWindowClickListener(clusterManager);
map.setOnMapClickListener(this);

【问题讨论】:

以下解决方案已过时,还需要重新集群。我找到了一个更好的解决方案及其工作***.com/questions/25684219/… 【参考方案1】:

mClusterManager.cluster();

添加新项目后强制重新聚类项目。

【讨论】:

当我在 AsyncTask 完成后执行它时,这对我有用,它会用项目填充 clusterManager。 工作适合我。如果您需要重新创建所有集群,请尝试以下方式:mClusterManager.clearItems(); addItems();(你的逻辑你如何填充管理器) mClusterManager.cluster(); 仅在将项目添加到地图时有效。不是在它们更新并需要重新绘制时【参考方案2】:

似乎我找到了解决方法。

ClusterManager 使用渲染器,在这种情况下,它继承自 DefaultClusterRenderer,后者使用内部缓存,即添加到地图的标记缓存。您可以直接访问地图上添加的标记,我不使用信息窗口,所以我添加标记 options.title() 一个 ID 以便以后找到这个标记,所以:

@Override
protected void onBeforeClusterItemRendered(TweetClusterItem item, MarkerOptions markerOptions) 

     .... Blabla code....          
            markerOptions.title(Long.toString(tweet.getId()));
     .... Blabla code....



当我想重新加载 clusterItem 时,我会调用这个方法:

/**
  * Workarround to repaint markers
  * @param item item to repaint
 */
  public void reloadMarker(TweetClusterItem item) 

        MarkerManager.Collection markerCollection = clusterManager.getMarkerCollection();
        Collection<Marker> markers = markerCollection.getMarkers();
        String strId = Long.toString(item.getTweet().getId());
        for (Marker m : markers) 
            if (strId.equals(m.getTitle())) 
                m.setIcon( ICON TO SET);
                break;
            
        

    

也许有点笨拙,但它有效,我没有找到任何其他方法来做到这一点。如果您发现了另一种更好的方法,请分享:)

【讨论】:

clusterManager.getMarkerCollection() 在我的情况下返回一个空列表。但地图将标记显示为集群。 @Sunny 不确定是否为时已晚,但要获得集群,您需要调用 clusterManager.getClusterMarkerCollection() 旧帖子,但这种方法对我有用。您必须记住的想法是集群和标记保存在不同的集合中。更新标记集合中的标记将更改簇外的标记,而簇中的标记将保持不变。为我清除项目或地图导致项目永远不会被重绘。 我想更新标记位置,使用了m.setPosition 函数,它确实更新了它们,但是当我缩小(集群形式)并重新放大(集群分解成单独的标记时,它们的位置被重置)。如何使该职位更新永久化?【参考方案3】:

您可以使用 DefaultClusterRenderer 的 getMarker()、getCluster() 和 getClusterItem()(设置您自己的渲染器以访问渲染器对象)在 O(1) 中获取与其集群或集群项目相对应的特定标记,反之亦然。

根据需要使用这些方法更改项目的标记。

   ...
   DefaultClusterRenderer mRenderer = ...
   mClusterManager.setRenderer(mRenderer);
   ...

public void reloadMarker(ClusterItem item) 
    mRenderer.getMarker(item).setIcon(YOUR_ICON);

我不建议将它们保存在其他任何地方,因为这些方法返回渲染器的缓存对象。

【讨论】:

【参考方案4】:

我遇到了同样的问题。没有一个建议的解决方案对我有用。我创建了一个扩展 DefaultClusterRenderer 的类,并添加了公共方法 updateClusterItem(ClusterItem clusterItem),这将强制重新渲染与该 ClusterItem 关联的标记(适用于集群和集群项目)。

import android.content.Context;
import android.support.annotation.CallSuper;
import android.support.annotation.NonNull;

import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.maps.android.clustering.Cluster;
import com.google.maps.android.clustering.ClusterItem;
import com.google.maps.android.clustering.ClusterManager;
import com.google.maps.android.clustering.view.DefaultClusterRenderer;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;


public abstract class CustomClusterRenderer<T extends ClusterItem>
        extends DefaultClusterRenderer<T> 

    private ClusterManager<T> mClusterManager;
    private Map<T, Marker> mClusterMap = new HashMap<>();

    public CustomClusterRenderer(Context context, GoogleMap map,
                                 ClusterManager<T> clusterManager) 
        super(context, map, clusterManager);
        mClusterManager = clusterManager;
    


    @Override
    @CallSuper
    protected void onClusterItemRendered(T clusterItem, Marker marker) 
        super.onClusterItemRendered(clusterItem, marker);
        mClusterMap.remove(clusterItem);
        cleanCache();
    

    @Override
    @CallSuper
    protected void onClusterRendered(Cluster<T> cluster, Marker marker) 
        super.onClusterRendered(cluster, marker);
        for (T clusterItem : cluster.getItems()) 
            mClusterMap.put(clusterItem, marker);
        
        cleanCache();
    

    public void updateClusterItem(T clusterItem) 
        Marker marker = getMarker(clusterItem);
        boolean isCluster = false;
        if (marker == null) 
            marker = mClusterMap.get(clusterItem);
            isCluster = marker != null;
        
        if (marker != null) 
            MarkerOptions options = getMarkerOptionsFromMarker(marker);
            if (isCluster) 
                Cluster cluster = getCluster(marker);
                onBeforeClusterRendered(cluster, options);
             else 
                onBeforeClusterItemRendered(clusterItem, options);
            
            loadMarkerWithMarkerOptions(marker, options);
        
    

    private void cleanCache() 
        ArrayList<T> deleteQueue = new ArrayList<>();
        Collection<Marker> clusterMarkers = mClusterManager
                .getClusterMarkerCollection().getMarkers();

        for (T clusterItem : mClusterMap.keySet()) 
            if (!clusterMarkers.contains(mClusterMap.get(clusterItem))) 
                deleteQueue.add(clusterItem);
            
        

        for (T clusterItem : deleteQueue) 
            mClusterMap.remove(clusterItem);
        
        deleteQueue.clear();
    

    private MarkerOptions getMarkerOptionsFromMarker(@NonNull Marker marker) 
        MarkerOptions options = new MarkerOptions();

        options.alpha(marker.getAlpha());
        options.draggable(marker.isDraggable());
        options.flat(marker.isFlat());
        options.position(marker.getPosition());
        options.rotation(marker.getRotation());
        options.title(marker.getTitle());
        options.snippet(marker.getSnippet());
        options.visible(marker.isVisible());
        options.zIndex(marker.getZIndex());

        return options;
    

    private void loadMarkerWithMarkerOptions(@NonNull Marker marker,
                                             @NonNull MarkerOptions options) 
        marker.setAlpha(options.getAlpha());
        marker.setDraggable(options.isDraggable());
        marker.setFlat(options.isFlat());
        marker.setPosition(options.getPosition());
        marker.setRotation(options.getRotation());
        marker.setTitle(options.getTitle());
        marker.setSnippet(options.getSnippet());
        marker.setVisible(options.isVisible());
        marker.setZIndex(options.getZIndex());
        marker.setIcon(options.getIcon());
        marker.setAnchor(options.getAnchorU(), options.getAnchorV());
        marker.setInfoWindowAnchor(options.getInfoWindowAnchorU(), options.getInfoWindowAnchorV());
    


【讨论】:

这是解决这种悲伤的 gmap/集群情况的最佳解决方案 这绝对是这里最好的答案【参考方案5】:

我遇到了同样的问题。我在我的 DefaultClusterRenderer 子类的 onBeforeClusterItemRendered 中进行自定义渲染,这也使情况更加复杂。

我的解决方案是创建我的 DefaultClusterRenderer 子类的新实例并再次在 ClusterManager 上调用 setRenderer。这会转储所有缓存的图标并重新创建所有内容。

它是 hacky、蛮力和令人讨厌的低效,但它确实有效。这是我发现的唯一可行的方法,因为该库似乎对此没有明确的支持。

【讨论】:

这种方法对我有用。重新分配我的自定义渲染器实例可以解决问题,但感觉不对,感觉可能会导致内存泄漏。调用 mClusterManager.cluster() 也可以,而且似乎是一种更好的方法。【参考方案6】:

mClusterManager.cluster(); 不适合我

尽管如此:

if (mMap != null) 
    CameraPosition currentCameraPosition = mMap.getCameraPosition();
    mMap.moveCamera(CameraUpdateFactory.newCameraPosition(currentCameraPosition));

这触发了 onCameraChange 调用,我已经在其中执行 mClusterManager.clearItems()... mClusterManager.addItem(..) - 用于可见区域内的对象... mClusterManager.cluster()

我的情况是,当返回显示地图的片段时,图钉正在消失(- 仅在某些设备上,例如 Nexus 7,没有自动调用 OnCameraChange)

【讨论】:

你能发布一个在 onCameraChange 中使用的代码示例吗?我希望只有在可见区域中渲染标记才能提高我的应用的性能【参考方案7】:

我注意到标记仅在放大或缩小时出现,因此我使用所有旧值设置了一个新的相机位置,除了轻微的缩放变化。

    CameraPosition currentCameraPosition = googleMap.getCameraPosition();
    CameraPosition cameraPosition = new CameraPosition(currentCameraPosition.target, currentCameraPosition.zoom - .1f, currentCameraPosition.tilt, currentCameraPosition.bearing);
    googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));

【讨论】:

【参考方案8】:

我使用扩展 DefaultClusterRenderer 的 CustomRenderer 的解决方案

 protected void onClusterItemRendered(T clusterItem, Marker marker) 
    marker.setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));

【讨论】:

以上是关于ClusterManager 重绘 Google maps v2 utils 的标记的主要内容,如果未能解决你的问题,请参考以下文章

ClusterManager onMarkerClickListener 用于非聚集标记

Google Maps API 重绘地理编码界限

如何在 Google Maps API 上强制重绘?

如何在 android 中获得 ClusterManager 点击和 ClusterManager 项目点击

ClusterManager setOnCameraIdleListener

从 ClusterManager 获取标记