android-maps-utils
android-maps-utils copied to clipboard
Updating items clusterItems : Clear items and re-adding doesn't always work
I am having an issue with this library. Whenever I clear the items from the cluster manager and add a new set, some single pins are not redrawn. Here's what I am doing:
private void displayMarkers(List markersToShowOnMap) { gMap.clear(); clusterManager.clearItems(); clusterManager.cluster(); //if I disable this it doesn't work either mClusterManager.addItems(markersToShowOnMap); mClusterManager.cluster(); }
If I call this method with the same set of markers, while zoomed in such that some clusters are displayed and some single markers are displayed, the single markers disappear! I am using the DefaultClusterRenderer. I have logged the renderer and onBeforeClusterItemRendered is not being called a second time after clearing and re-adding items. If i zoom out and back in, the missing markers re-appear.
@juamor86 Thanks for reaching out! Starting with v1 of the library, you'll need to override onClusterItemUpdated() in addition to onBeforeClusterItemRendered() - the general implementation can be the same. Similarly, you should override onClusterUpdated() in addition to onBeforeClusterRendered() - the general implementation can also be the same (Note that the implementation can't be identical, because you need to operate on a Marker instead of MarkerOptions, but the code is very close.).
You can see examples in the demo app CustomMarkerClusteringDemoActivity: https://github.com/googlemaps/android-maps-utils/blob/master/demo/src/main/java/com/google/maps/android/utils/demo/CustomMarkerClusteringDemoActivity.java
The reason for needing to override the new methods is that some markers are cached for performance reasons, and therefore they might be updated instead of being rendered. If you don't override these *Updated() methods, the default implementation of these methods will execute, which might do strange things you're not expecting.
I've just added more documentation to the migration guide in the README in PR https://github.com/googlemaps/android-maps-utils/pull/734 on this topic.
Could you give that a try and see if it fixes the issue?
I only had the method onBeforeClusterItemRenderedoverride but I now added this three method
@Override protected void onBeforeClusterItemRendered(Centres centres, MarkerOptions markerOptions) { markerOptions.icon(getItemIcon(centres)); }
@Override
protected void onClusterItemUpdated(Centres centres, Marker marker) {
marker.setIcon(getItemIcon(centres));
}
@Override
protected void onBeforeClusterRendered(Cluster<Centres> cluster, MarkerOptions markerOptions) {
}
@Override
protected void onClusterUpdated(Cluster<Centres> cluster, Marker marker) {
}
I would like that the cluster keep the colors from before, definitely the item marker with markerOptions.icon(getItemIcon(centres)); but the cluster collection keep the same color default, what must I do it?
I could do the override onBeforeClusterRendered onClusterUpdated to keep the item cluster default but this moment the method onClusterItemUpdated is doing crash the app
this my code of my custom DefaultClusterRenderer
public class CentresMarkerRenderer extends DefaultClusterRenderer<Centres> {
private static final int[] BUCKETS = {10, 20, 50, 100, 200, 500, 1000};
private SparseArray<BitmapDescriptor> mIcons = new SparseArray<>();
private ShapeDrawable mColoredCircleBackground;
private final IconGenerator mIconGenerator;
private final float mDensity;
private Context context;
public CentresMarkerRenderer(Context ctx, GoogleMap map, ClusterManager<Centres> clusterManager) {
super(ctx, map, clusterManager);
context = ctx;
mDensity = context.getResources().getDisplayMetrics().density;
mIconGenerator = new IconGenerator(context);
mIconGenerator.setContentView(makeSquareTextView(context));
mIconGenerator.setTextAppearance(R.style.amu_ClusterIcon_TextAppearance);
mIconGenerator.setBackground(makeClusterBackground());
}
@Override
protected void onBeforeClusterItemRendered(Centres centres, MarkerOptions markerOptions) {
markerOptions.icon(getDescriptorForMarker(centres));
}
@Override
protected void onClusterItemUpdated(Centres centres, Marker marker) {
marker.setIcon(getDescriptorForMarker(centres));
}
@Override
protected void onBeforeClusterRendered(Cluster<Centres> cluster, MarkerOptions markerOptions) {
markerOptions.icon(getDescriptorForCluster(cluster));
}
@Override
protected void onClusterUpdated(Cluster<Centres> cluster, Marker marker) {
marker.setIcon(getDescriptorForCluster(cluster));
}
@Override
protected boolean shouldRenderAsCluster(Cluster cluster) {
return cluster.getSize() > 1; // Always render clusters.
}
private BitmapDescriptor getDescriptorForMarker(Centres centres) {
return ViewUtil.getBitmapDescriptor(context, centres.getIconMarker(), ViewUtil.sizeIconMarkerDefault);
}
protected BitmapDescriptor getDescriptorForCluster(@NonNull Cluster<Centres> cluster) {
int bucket = getBucket(cluster);
BitmapDescriptor descriptor = mIcons.get(bucket);
if (descriptor == null) {
mColoredCircleBackground.getPaint().setColor(getColor(bucket));
descriptor = BitmapDescriptorFactory.fromBitmap(mIconGenerator.makeIcon(getClusterText(bucket)));
mIcons.put(bucket, descriptor);
}
return descriptor;
}
private LayerDrawable makeClusterBackground() {
mColoredCircleBackground = new ShapeDrawable(new OvalShape());
ShapeDrawable outline = new ShapeDrawable(new OvalShape());
outline.getPaint().setColor(0x80ffffff); // Transparent white.
LayerDrawable background = new LayerDrawable(new Drawable[]{outline, mColoredCircleBackground});
int strokeWidth = (int) (mDensity * 3);
background.setLayerInset(1, strokeWidth, strokeWidth, strokeWidth, strokeWidth);
return background;
}
private SquareTextView makeSquareTextView(Context context) {
SquareTextView squareTextView = new SquareTextView(context);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
squareTextView.setLayoutParams(layoutParams);
squareTextView.setId(R.id.amu_text);
int twelveDpi = (int) (12 * mDensity);
squareTextView.setPadding(twelveDpi, twelveDpi, twelveDpi, twelveDpi);
return squareTextView;
}
}
the error is java.lang.IllegalArgumentException: Unmanaged descriptor at com.google.maps.api.android.lib6.common.m.d(:com.google.android.gms.policy_maps_dynamite@[email protected]:0) at com.google.maps.api.android.lib6.impl.q.c(:com.google.android.gms.policy_maps_dynamite@[email protected]:1) at com.google.maps.api.android.lib6.impl.cy.a(:com.google.android.gms.policy_maps_dynamite@[email protected]:20) at com.google.android.gms.maps.model.internal.o.a(:com.google.android.gms.policy_maps_dynamite@[email protected]:19) at ch.onTransact(:com.google.android.gms.policy_maps_dynamite@[email protected]:4) at android.os.Binder.transact(Binder.java:675) at com.google.android.gms.internal.maps.zza.zzb(SourceFile:20) at com.google.android.gms.internal.maps.zzv.zzg(SourceFile:80) at com.google.android.gms.maps.model.Marker.setIcon(SourceFile:30) at es.juntadeandalucia.msspa.centros.ui.centres.centresMap.CentresMarkerRenderer.onClusterItemUpdated(SourceFile:54) at es.juntadeandalucia.msspa.centros.ui.centres.centresMap.CentresMarkerRenderer.onClusterItemUpdated(SourceFile:28)
the method for convert a svg to bitmap is this but I think this method not is the problem
public static BitmapDescriptor getBitmapDescriptor(Context context, int id, int size) { Drawable vectorDrawable = context.getDrawable(id); int h = ViewUtil.dpToPx(size); int w = ViewUtil.dpToPx(size); if(vectorDrawable == null){ return BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN); } vectorDrawable.setBounds(0, 0, w, h); Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bm); vectorDrawable.draw(canvas); return BitmapDescriptorFactory.fromBitmap(bm); }
@juamor86 Hmmm, I'm not sure. Are you able to reproduce this based on the demo app code?
@juamor86 Looking back at your original code:
private void displayMarkers(List markersToShowOnMap) {
gMap.clear();
clusterManager.clearItems();
clusterManager.cluster(); //if I disable this it doesn't work either
mClusterManager.addItems(markersToShowOnMap);
mClusterManager.cluster();
}
Please try removing the gMap.clear() line to let the cluster manager remove the markers, and remove the 1st cluster line too:
private void displayMarkers(List markersToShowOnMap) {
clusterManager.clearItems();
clusterManager.addItems(markersToShowOnMap);
clusterManager.cluster();
}
I got same issue(Unmanaged descriptor) in sometime, I can't reproduce it.
@a1573595 Are you calling GoogleMap.clear() anywhere in your app code? Or removing any clustered markers directly in your app code via the Marker object?
If you clear markers in your own app code, it can confuse the clustering manager, as it thinks the markers still exist on the map, and can result in this error.
@a1573595 Are you calling
GoogleMap.clear()anywhere in your app code? Or removing any clustered markers directly in your app code via theMarkerobject?If you clear markers in your own app code, it can confuse the clustering manager, as it thinks the markers still exist on the map, and can result in this error.
Yes, I use Clustering marker, normal marker, PolyLine and Circle on Map. I wrote a method to clear all data, but problem still exists.
@Override
public void clearMap() {
mClusterManager.clearItems();
map.clear();
}
@a1573595 Hmmm, that should work as long as you're clearing the items in the cluster manager first. Can you try removing the call to map.clear() and see if you still get the error? I know this is hard to test when you can't consistently reproduce it.
@a1573595 Hmmm, that should work as long as you're clearing the items in the cluster manager first. Can you try removing the call to
map.clear()and see if you still get the error? I know this is hard to test when you can't consistently reproduce it.
Sorry, I got this problem on Crashlytics in other device. I can't reproduce it.
Closing as stale; please reopen if you still see this behavior.
I have a similar issue. I am doing what @barbeau says
clusterManager.clearItems();
clusterManager.addItems(markersToShowOnMap);
clusterManager.cluster();
and also override onClusterItemUpdated() and the other methods.
I have noticed that my markers will update their location correctly on the map if I zoom out to cluster them and then zoom in again, then their location will be correct.
Any Idea of what can be happening? I am without ideas right now
Reopening based on @danielstr's comment
private void initClusterManager(){
if (mClusterManager != null) {
mClusterManager.getMarkerCollection().clear();
mClusterManager.clearItems();
mClusterManager.cluster();
}
// init Cluster Manager here
mClusterManager = new ClusterManager<>(context, myGoogleMap);
mClusterManager.setRenderer(new CustomMapClusterRender());
myGoogleMap.setOnCameraIdleListener(mClusterManager);
mClusterManager.setOnClusterClickListener(this);
mClusterManager.setOnClusterInfoWindowClickListener(this);
mClusterManager.setOnClusterItemClickListener(this);
mClusterManager.setOnClusterItemInfoWindowClickListener(this);
readItems(eventsList);
mClusterManager.cluster();
}
before init with new cluster manager please have a check like this and it will clear all the old markers and clusterManager items worked fine for me
I have a similar issue. I am doing what @barbeau says
clusterManager.clearItems(); clusterManager.addItems(markersToShowOnMap); clusterManager.cluster();and also override
onClusterItemUpdated()and the other methods. I have noticed that my markers will update their location correctly on the map if I zoom out to cluster them and then zoom in again, then their location will be correct.Any Idea of what can be happening? I am without ideas right now
private void initClusterManager(){
if (mClusterManager != null) {
mClusterManager.getMarkerCollection().clear();
mClusterManager.clearItems();
mClusterManager.cluster();
}
// init Cluster Manager here
mClusterManager = new ClusterManager<>(context, myGoogleMap);
mClusterManager.setRenderer(new CustomMapClusterRender());
myGoogleMap.setOnCameraIdleListener(mClusterManager);
mClusterManager.setOnClusterClickListener(this);
mClusterManager.setOnClusterInfoWindowClickListener(this);
mClusterManager.setOnClusterItemClickListener(this);
mClusterManager.setOnClusterItemInfoWindowClickListener(this);
readItems(eventsList);
mClusterManager.cluster();
} before init with new cluster manager please have a check like this and it will clear all the old markers and clusterManager items worked fine for me
if I do that all my markers wil dissapear when they should be redrawn. The only solution I found is, recreate the renderer before assing the new items, like this:
clusterManager.renderer = new CustomClusteringRenderer(...)
clusterManager.clearItems();
clusterManager.addItems(markersToShowOnMap);
clusterManager.cluster();
but this is not a viable solution
I found the issue. Was related to a migration from Java to Kotlin from an old CustomClusterItem. It was overriding the equals() method and there was the error. Nothing to do with the android maps utils library Sorry for the inconvenience and thanks to everyone who tried to help me!
Thanks for letting us know, @danielstr !