I'm new here, so any feedback is welcome. I'm trying to use a DefaultClusterRenderer to show some custom clusters, and I'm trying to get it not to cluster when the map is fully zoomed in. So I found this answer to the exact same question: Disable clustering at max zoom level with Googles android-maps-utils so I tried that code, but I'm getting a Not on the main thread error. Any help will be greatly appreciated. I have this in my constructor
public EspecieRenderer(Context context, GoogleMap map, ClusterManager<T> clusterManager) {
super(context, map, clusterManager);
this.mMap = map;
...
}
and then I'm doing
@Override
protected boolean shouldRenderAsCluster(Cluster<T> cluster) {
final float currentZoom = mMap.getCameraPosition().zoom;
final float currentMaxZoom = mMap.getMaxZoomLevel();
return currentZoom < currentMaxZoom && cluster.getSize() >= 10;
}
Here is how I'm initializing the cluster. I'm using threads because I have close to 200 species to load, each with one or more markers to add to the map
private void setUpClusterer() {
clusterManager = new ClusterManager<>(this, googleMap);
clusterManager.setRenderer(new EspecieRenderer<>(this, googleMap, clusterManager));
googleMap.setOnCameraChangeListener(clusterManager);
googleMap.setOnMarkerClickListener(clusterManager);
ArrayList<Especie> especies = (ArrayList<Especie>) Especie.list(this);
for (Especie especie : especies) {
ExecutorService queue = Executors.newSingleThreadExecutor();
queue.execute(new EspecieLoader(this, especie));
}
}
Where EspecieLoader
public class EspecieLoader implements Runnable {
private MainActivity context;
private Especie especie;
public EspecieLoader(MainActivity mainActivity, Especie especie) {
this.context = mainActivity;
this.especie = especie;
}
@Override
public void run() {
List<Foto> fotos = Foto.findAllByEspecieWithdCoords(context, especie);
String nombre = especie.getNombreCientifico();
for (Foto foto : fotos) {
LatLng latLng = new LatLng(foto.getLatitud(), foto.getLongitud());
Bitmap bitmap;
String path = "new/" + foto.getPath().replaceAll("-", "_").toLowerCase();
try {
bitmap = ResourcesHelper.getEncyclopediaAssetByName(context, path);
EspecieMarker especieMarker = new EspecieMarker(nombre, bitmap, latLng);
context.addSpeciesMarker(especieMarker);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
I'm getting this when I try to run my app:
07-16 19:18:41.402 31151-32075/com.lzm.Cajas E/AndroidRuntime: FATAL EXCEPTION: Thread-47332
Process: com.lzm.Cajas, PID: 31151
java.lang.IllegalStateException: Not on the main thread
2at maps.f.g.b(Unknown Source)
at maps.z.F.a(Unknown Source)
at maps.af.t.a(Unknown Source)
at vl.onTransact(:com.google.android.gms.DynamiteModulesB:51)
at android.os.Binder.transact(Binder.java:387)
at com.google.android.gms.maps.internal.IGoogleMapDelegate$zza$zza.getCameraPosition(Unknown Source)
at com.google.android.gms.maps.GoogleMap.getCameraPosition(Unknown Source)
at com.lzm.Cajas.map.EspecieRenderer.shouldRenderAsCluster(EspecieRenderer.java:62)
at com.google.maps.android.clustering.view.DefaultClusterRenderer$RenderTask.run(DefaultClusterRenderer.java:416)
at java.lang.Thread.run(Thread.java:818)
2022 UPDATE - Since the previous answer is a bit messy and the google maps camera events have changed here's a more up-to-date and elegant solution:
Extend ClusterManager
end expose a property that indicates whether the markers should be clustered according to the zoom level and update it in the onCameraIdle
event.
open class ZoomClusterManager<T : ClusterItem>(
context: Context,
private val map: GoogleMap
) : ClusterManager<T>(context, map) {
companion object {
private const val CLUSTER_MAX_ZOOM_LEVEL = 9
}
private var _shouldClusterZoom: Boolean = true
val shouldClusterZoom get() = _shouldClusterZoom
override fun onCameraIdle() {
super.onCameraIdle()
_shouldClusterZoom = map.cameraPosition.zoom < CLUSTER_MAX_ZOOM_LEVEL
}
}
Depending on the usecase you might want to have CLUSTER_MAX_ZOOM_LEVEL
as property in the constructor for better reusability of the class.
Then extend DefaultClusterRenderer
as well. In the constructor pass your extended version of ClusterManager, override shouldRenderAsCluster
and check the previously exposed property like this:
open class ZoomClusterRenderer<T : ClusterItem>(
context: Context,
map: GoogleMap,
private val clusterManager: ZoomClusterManager<T>
) : DefaultClusterRenderer<T>(context, map, clusterManager) {
override fun shouldRenderAsCluster(cluster: Cluster<T>): Boolean {
return super.shouldRenderAsCluster(cluster) && clusterManager.shouldClusterZoom
}
}
In the end wire everything up in your fragment where you're setting up the map:
val clusterManager = ZoomClusterManager<ClusterItem>(requireContext(), googleMap)
clusterManager.renderer = ZoomClusterRenderer(requireContext(), googleMap, clusterManager)
googleMap.setOnCameraIdleListener(clusterManager)
Old answer - I've run into the same need just today. This is my solution: Since you can't set setOnCameraChangeListener twice, remove
googleMap.setOnCameraChangeListener(clusterManager);
and replace it with
googleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
@Override
public void onCameraChange(CameraPosition cameraPosition) {
shouldCluster_zoom = cameraPosition.zoom < 9; //disables the cluster at 9 and higher zoom levels
clusterManager.onCameraChange(cameraPosition); //Replaces googleMap.setOnCameraChangeListener(clusterManager);
}
});
"shouldCluster_zoom
" obviously is a global variable, static and boolean.
Now in "shouldRenderAsCluster
" method check, according to the zoom, if markers should be clustered:
@Override
protected boolean shouldRenderAsCluster(Cluster<T> cluster) {
return super.shouldRenderAsCluster(cluster) && Your_class_name.shouldCluster_zoom;
}
Editing on top of @Alberto97's answer:
OnCameraChange method is now deprecated. They replaced GoogleMap.OnCameraChangeListener() with three camera listeners :
I would use OnCameraIdleListener
googleMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
@Override
public void onCameraIdle() {
shouldCluster_zoom = cameraPosition.zoom < 9;
clusterManager.onCameraIdle();
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With