Google Maps Utils Cluster Manager cluster markers together into clusters (round circle icon with number) to avoid showing too many markers.
The ClusterManager helps you manage multiple markers at different zoom levels. This means you can put a large number of markers on a map without making the map hard to read. When a user views the map at a high zoom level, the individual markers show on the map. When the user zooms out to a lower zoom level, the markers gather together into clusters, to make viewing the map easier.
This article focus on how change the text, size and color of the cluster icons.
Hack DefaultClusterRenderer
DefaultClusterRenderer does not expose most of its variables and functions (private only), so the only way to expose them is to copy the code and modify them with your own implementation.
Copy the code of DefaultClusterRenderer and rename the class as MyClusterRenderer.
- Edit
makeSquareTextView
to change font size, style and padding, which would affect the cluster oval icon's size. Remember to removemIconGenerator.setTextAppearance
to prevent text style to be overwritten. - Edit
getColor
to change the cluster oval icon's background color. - Edit
onBeforeClusterRendered
orgetClusterText
to change cluster oval icon's text.
public class MyClusterRenderer<T extends ClusterItem> implements ClusterRenderer<T> { ... public NumberClusterRenderer(Context context, GoogleMap map, ClusterManager<T> clusterManager) { mMap = map; mAnimate = true; mDensity = context.getResources().getDisplayMetrics().density; mIconGenerator = new IconGenerator(context); mIconGenerator.setContentView(makeSquareTextView(context)); // comment this line to prevent text style being applied and overwriting makeSquareTextView settings // mIconGenerator.setTextAppearance(R.style.amu_ClusterIcon_TextAppearance); mIconGenerator.setBackground(makeClusterBackground()); mClusterManager = clusterManager; } ... // the oval shape of cluster icon size depends mostly on the text and padding size private SquareTextView makeSquareTextView(Context context) { SquareTextView squareTextView = new SquareTextView(context); // added the following 3 lines to change text size, color and bold // I make the text smaller, color and bold is required since we removed R.style.amu_ClusterIcon_TextAppearance squareTextView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10); squareTextView.setTextColor(Color.WHITE); squareTextView.setTypeface(null, Typeface.BOLD); ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); squareTextView.setLayoutParams(layoutParams); squareTextView.setId(R.id.amu_text); // initial is 12dp, I resize the round shape padding to 6dp int twelveDpi = (int) (6 * mDensity); squareTextView.setPadding(twelveDpi, twelveDpi, twelveDpi, twelveDpi); return squareTextView; } ... protected int getColor(int clusterSize) { /* final float hueRange = 220; final float sizeRange = 300; final float size = Math.min(clusterSize, sizeRange); final float hue = (sizeRange - size) * (sizeRange - size) / (sizeRange * sizeRange) * hueRange; return Color.HSVToColor(new float[]{ hue, 1f, .6f }); */ // I use Cyan color palette: https://material.io/guidelines/style/color.html#color-color-palette // refer to variable BUCKETS for available cluster size String color = "#80DEEA"; switch(clusterSize) { case 10: color = "#4DD0E1"; break; case 20: color = "#26C6DA"; break; case 50: color = "#00BCD4"; break; case 100: color = "#00ACC1"; break; case 200: color = "#0097A7"; break; case 500: color = "#00838F"; break; case 1000: color = "#006064"; break; } return Color.parseColor(color); } ... protected void onBeforeClusterRendered(Cluster<T> cluster, MarkerOptions markerOptions) { int bucket = getBucket(cluster); BitmapDescriptor descriptor = mIcons.get(bucket); if (descriptor == null) { mColoredCircleBackground.getPaint().setColor(getColor(bucket)); // you can edit/replace getClusterText to change text of cluster icon descriptor = BitmapDescriptorFactory.fromBitmap(mIconGenerator.makeIcon(getClusterText(bucket))); mIcons.put(bucket, descriptor); } // TODO: consider adding anchor(.5, .5) (Individual markers will overlap more often) markerOptions.icon(descriptor); } ... }
Load our custom MyClusterRenderer
.
mapFragment.getMapAsync(new OnMapReadyCallback() { @Override public void onMapReady(final GoogleMap map) { ClusterManager clusterManager = new ClusterManager(activity, map); final MyClusterRenderer renderer = new NumberClusterRenderer(activity, map, clusterManager); // use this to show cluster only without markers renderer.setMinClusterSize(0); clusterManager.setRenderer(renderer); }});