I am trying to change the icon for a selected marker. Neither calling ClusterManager.cluster, nor MapView.invalidate will cause the ClusterRenderer to render new pins.
protected void onBeforeClusterItemRendered(FacilityClusterItem item, MarkerOptions markerOptions) {
Facility facility = item.getFacility();
int pinId = 0;
if (TextUtils.equals(facility.getId(), mSelectedFacilityId)) {
mIconGenerator.setBackground(mContext.getResources().getDrawable(R.drawable.map_pin_white));
pinId = facility.getFacilityType().getSelectedMapPinId();
} else {
mIconGenerator.setBackground(mContext.getResources().getDrawable(R.drawable.map_pin_purple));
pinId = facility.getFacilityType().getMapPinId();
}
icon = BitmapDescriptorFactory.fromResource(pinId);
markerOptions.icon(icon);
}
I think this is because this callback happens prior to a cluster item map marker being added to the map object.
So you only get to override the icon once, right when the marker is created for the first time.
To get the item to update you'd have to remove it and add it back, I think.
I did something different, I manually overrode the callback for the cluster manager's marker items:
clusterManager.getMarkerCollection().setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
@Override
public boolean onMarkerClick(Marker marker) {
BitmapDescriptor icon = ...;
marker.setIcon(icon);
}
}
Although I am currently debugging some issues with this approach, where when that marker whose icon has been set with setIcon gets removed from the map (via clusterManager.clear() or some other means), the application crashes with an error due to a java.lang.IllegalArgumentException: Released unknown bitmap reference.
@jakeonrails @daschewie may be related https://github.com/googlemaps/android-maps-utils/issues/90
This is also my use case.
"Changing the selected marker's icon"
Any stable way to achieve this?
This commit makes it possible: https://github.com/googlemaps/android-maps-utils/commit/49d42d65c1eb5f92ce4239ab82ad7915ba1d9e69#
@jakeonrails You can theoretically override onClusterItemRendered to achieve this, which allows you to set the icon on the existing Marker instead of MarkerOptions. The gotcha with this approach is that the call happens as part of CreateMarkerTask#perform which should run as part of the RenderTask; only the latter does not actually do anything if the sets of clusters did not change, according to equals. Now, this should be fine if you're diligent in overriding equals for your class implementing ClusterItem, only ClusterManager#setAlgorithm decides that it will slap a PreCachingAlgorithmDecorator on whatever you pass in (not that you asked for it), so that as far as I can see you cannot ever hope to convince the renderer that yes, in fact your clusters have changed ...
So what I think we really want is for ClusterManager to not try and be too clever about enhancing the algorithm you pass in, if the decorator actually breaks behaviour.
java.lang.IllegalArgumentException: Released unknown imageData reference
at com.google.a.a.ae.a(Unknown Source)
at com.google.maps.api.android.lib6.e.dn.c(Unknown Source)
at com.google.maps.api.android.lib6.e.ag.o(Unknown Source)
at com.google.maps.api.android.lib6.e.ag.a(Unknown Source)
at com.google.android.gms.maps.model.a.p.onTransact(SourceFile:51)
at android.os.Binder.transact(Binder.java:380)
at com.google.android.gms.maps.model.internal.zzf$zza$zza.remove(Unknown Source)
at com.google.android.gms.maps.model.Marker.remove(Unknown Source)
at com.google.maps.android.MarkerManager$Collection.remove(MarkerManager.java:163)
at com.google.maps.android.MarkerManager.remove(MarkerManager.java:140)
at com.google.maps.android.clustering.view.DefaultClusterRenderer$AnimationTask.onAnimationEnd(DefaultClusterRenderer.java:912)
at android.animation.ValueAnimator.endAnimation(ValueAnimator.java:1089)
at android.animation.ValueAnimator$AnimationHandler.doAnimationFrame(ValueAnimator.java:666)
at android.animation.ValueAnimator$AnimationHandler.run(ValueAnimator.java:682)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:767)
at android.view.Choreographer.doCallbacks(Choreographer.java:580)
at android.view.Choreographer.doFrame(Choreographer.java:549)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:753)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:135)
at android.app.ActivityThread.main(ActivityThread.java:5297)
at java.lang.reflect.Method.invoke(Native Method)
at java.lang.reflect.Method.invoke(Method.java:372)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:908)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:703)
Getting above error while using onClusterItemRendered. I am running async task to get image for marker:
protected void onClusterItemRendered(Poi clusterItem, Marker marker) {
super.onClusterItemRendered(clusterItem, marker);
try {
URL url = new URL(clusterItem.getImage());
AsyncDownloadImage async = new AsyncDownloadImage(marker);
async.execute(url);
} catch (MalformedURLException e) {
Log.e("CluserRenderer", "Wrong url address: " + clusterItem.getImage());
}
}
And in onPostExecute I do this (markerReference is WeakReference):
protected void onPostExecute(final Bitmap bitmap) {
Marker marker = markerReference.get();
if (bitmap != null && marker != null) {
mImageView.setImageBitmap(bitmap);
Bitmap icon = mIconGenerator.makeIcon();
try {
marker.setIcon(BitmapDescriptorFactory.fromBitmap(icon));
} catch (IllegalArgumentException e) {
Log.e("ClusterRenderer", "Couldn't set icon.");
}
}
}
Images for marker cluster items do render while zooming in, then while zooming out the cluster group is rendered for marker, then again while zooming in items do render correctly. Exception occurs when user do zoom in and zoom out fast when items did not render yet.
Keeping marker object instead of weakreference in async fixed issue for me. Additionaly I keep hashmap of already downloaded url/ bitmap in ClusterRenderer class. Whenever clusteritem gets rendered it checks hashmap for bitmaps.
Nope, still crashes, same error as before.
@DarekDeo +1
Currently working on same issue hopefully can figure something out.
I managed to get this working for showing a selected state on a marker. It requires extending the DefaultClusterRenderer class and overriding one method (onBeforeClusterItemRendered) and adding an onClusterItemClick listener.
public class MyCustomRenderer<T> extends DefaultClusterRenderer implements ClusterManager.OnClusterItemClickListener<MyClusterItem> {
private MyClusterItem currentClickedClusterItem;
public MyCustomRenderer(Context context, GoogleMap map, ClusterManager<MyClusterItem> clusterManager) {
super(context, map, clusterManager);
setOnClusterItemClickListener(this);
}
@Override
protected void onBeforeClusterItemRendered(ClusterItem item, MarkerOptions markerOptions) {
if (currentClickedClusterItem != null && item.equals(currentClickedClusterItem)) {
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
} else {
markerOptions.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
}
}
@Override
public boolean onClusterItemClick(MyClusterItem item) {
if (currentClickedClusterItem != null) {
getMarker(currentClickedStopItem).setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED));
}
currentClickedStopItem = item;
getMarker(item).setIcon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE));
return true;
}
}
getMarker() always returns null for me, no way to get this working...
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
This issue has been automatically marked as stale because it has not had recent activity. Please comment here if it is still valid so that we can reprioritize. Thank you!
Duplicate of #90, closing this issue in favor of that one instead. See the discussion there for possible workarounds in the meantime.
Most helpful comment
I managed to get this working for showing a selected state on a marker. It requires extending the DefaultClusterRenderer class and overriding one method (onBeforeClusterItemRendered) and adding an onClusterItemClick listener.