ClusterManager repaint markers of Google maps v2 utils
Asked Answered
A

8

21

I'm making a server request and when I receive the response from server, I'm executing on UI Thread a ClusterManager.addItem() but this items are not painting in the map, only when I make a zoom update (+,-) newly added items starts appearing. I also tried to debug the renderer, but onBeforeClusterRendered / onBeforeClusterItemRendered are not getting called until I update the zoom in map.

Any ideas how to refresh map/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);
Allaallah answered 9/3, 2014 at 19:45 Comment(1)
Solutions below are old and also requires reclustering. I found a better solution and its working #25684719Piranesi
A
17

Seems that I found a workaround.

ClusterManager uses a renderer, in this case it inherits from DefaultClusterRenderer which uses a internal cache, a cache of markers that are added to map. You can access directly to the added markers on the map, I don't use the info window, so i add marker options.title() an ID for later find this marker, so:

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

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


}

and when I want to reload the clusterItem I call this method:

/**
  * 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;
            }
        }

    }

Maybe is a little hacky but it works and I din't found any other way to do this. If you found another better way, please share :)

Allaallah answered 10/3, 2014 at 10:4 Comment(4)
clusterManager.getMarkerCollection() return an empty list in my case.But map show markers as cluster.Wardwarde
@Sunny Not sure if too late but to get the clusters you'll want to call clusterManager.getClusterMarkerCollection() insteadCapuchin
Old post but this approach worked for me. The think you have to remember is that clusters and markers are held in different collections. Updating markers in the marker collection will change those outside of clusters while those in clusters will remain the same. Clearing items or map for me resulted in items never being redrawn.Rory
I want to update marker position, used m.setPosition function, which did update them, but their position gets reset, when I zoom out (cluster forms) and zoom back in (cluster breaks into seperate markers). How to make that position update permanent?Incrustation
S
26

mClusterManager.cluster();

force re-clustering items when you after added new item.

Selia answered 14/3, 2014 at 2:52 Comment(3)
This works for me when I execute it after the AsyncTask is finished, which populates the clusterManager with items.Chelate
Work's for me. In case you need to recreate all clusters, try this way: mClusterManager.clearItems(); addItems();(your logic how you fill manager) mClusterManager.cluster();Sadirah
Works only when items are added to the map. Not when they are updated and need to be repaintedAdverb
A
17

Seems that I found a workaround.

ClusterManager uses a renderer, in this case it inherits from DefaultClusterRenderer which uses a internal cache, a cache of markers that are added to map. You can access directly to the added markers on the map, I don't use the info window, so i add marker options.title() an ID for later find this marker, so:

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

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


}

and when I want to reload the clusterItem I call this method:

/**
  * 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;
            }
        }

    }

Maybe is a little hacky but it works and I din't found any other way to do this. If you found another better way, please share :)

Allaallah answered 10/3, 2014 at 10:4 Comment(4)
clusterManager.getMarkerCollection() return an empty list in my case.But map show markers as cluster.Wardwarde
@Sunny Not sure if too late but to get the clusters you'll want to call clusterManager.getClusterMarkerCollection() insteadCapuchin
Old post but this approach worked for me. The think you have to remember is that clusters and markers are held in different collections. Updating markers in the marker collection will change those outside of clusters while those in clusters will remain the same. Clearing items or map for me resulted in items never being redrawn.Rory
I want to update marker position, used m.setPosition function, which did update them, but their position gets reset, when I zoom out (cluster forms) and zoom back in (cluster breaks into seperate markers). How to make that position update permanent?Incrustation
L
3

You can get specific markers that correspond to their cluster or cluster items and vice versa in O(1) using DefaultClusterRenderer's getMarker(), getCluster() and getClusterItem() (set your own renderer to access the renderer object).

Use these methods to change the markers of your items whenever you need.

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

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

I wouldn't recommend saving them anywhere else though, since those methods return the renderer's cache objects.

Lidalidah answered 12/8, 2015 at 19:8 Comment(1)
mRenderer.getMarker(item).setIcon(YOUR_ICON); sometimes will crash.Groceryman
H
3

I was having the same exact problem. None of the suggested solutions were working for me. I made a class which extends the DefaultClusterRenderer and adds the public method updateClusterItem(ClusterItem clusterItem) which will force the Marker associated with that ClusterItem to be re-rendered (works with both clusters and cluster items).

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());
    }

}
Hidalgo answered 17/8, 2016 at 21:50 Comment(3)
this is the best solution for this sad gmap/cluster situationLamonica
This is definitely the best answer hereBurkhart
This is an efficiency problemGroceryman
Z
2

I had the same problem. It was also compounded by the fact I'm doing custom rendering in onBeforeClusterItemRendered on my DefaultClusterRenderer subclass.

My solution was to create a new instance of my DefaultClusterRenderer subclass and call setRenderer on the ClusterManager again. This dumps all the cached icons & recreates everything.

It's hacky, brute force and annoyingly inefficient, but it does work. It was the only approach I found that worked since the library seems to have no explicit support for this.

Zirconium answered 17/9, 2014 at 16:31 Comment(1)
This approach worked for me. Re-assigning my custom renderer instance did the trick but feels wrong and feels like it could lead to a memory leak. Calling mClusterManager.cluster() works as well and seems like a better approach.Hispania
C
2

mClusterManager.cluster(); didn't work for me

this did though:

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

This triggered an onCameraChange call, where I was already doing mClusterManager.clearItems()... mClusterManager.addItem(..) - for objects inside the visible region... mClusterManager.cluster()

The context for me was that the pins were disappearing when returning back to the fragment displaying the map (- only on certain devices e.g. Nexus 7, where there was no automatic call to OnCameraChange)

Clanton answered 3/11, 2015 at 18:37 Comment(1)
Can you post an example of the code used inside onCameraChange? I'm hoping only rendering markers in the visible region could increase performance for my appHispania
A
0

I noticed that the marker only showed up when zooming in or out, so I set a new camera position with all the old values except for a slight zoom change.

    CameraPosition currentCameraPosition = googleMap.getCameraPosition();
    CameraPosition cameraPosition = new CameraPosition(currentCameraPosition.target, currentCameraPosition.zoom - .1f, currentCameraPosition.tilt, currentCameraPosition.bearing);
    googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
Alamo answered 8/2, 2020 at 22:38 Comment(0)
T
-2

My solution with a CustomRenderer that extends DefaultClusterRenderer

 protected void onClusterItemRendered(T clusterItem, Marker marker) {
    marker.setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN));
}
Tuna answered 18/10, 2015 at 21:25 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.