21

Is there any method to change the background color of the cluster item? (the one that displays the count of the markers, like 100+, 200+ ...). I tried to look into the source code of the ClusterManager but could not find any option to change the color, but maybe someone here knows how to do that. I basically want to "materialify" those colors a bit.

asked Jun 21, 2015 at 18:33

6 Answers 6

35

I was able to get a rough implementation working by using this demo from the library samples as a guide.

I used the lens icon from the Material Design Icons from here. After downloading the lens zip I put ic_lens_black_24dp.png under the drawable folder. Then I used the Drawable.setColorFilter() method to change the default color in the code.

I was also able to change the default Marker color, and figured I would include that as well here.

First, set a Renderer by calling setRenderer():

 mClusterManager.setRenderer(new MyClusterRenderer(this, mMap,
 mClusterManager));

Then, define the MyClusterRenderer class:

public class MyClusterRenderer extends DefaultClusterRenderer<MyItem> {
 private final IconGenerator mClusterIconGenerator = new IconGenerator(getApplicationContext());
 public MyClusterRenderer(Context context, GoogleMap map,
 ClusterManager<MyItem> clusterManager) {
 super(context, map, clusterManager);
 }
 @Override
 protected void onBeforeClusterItemRendered(MyItem item,
 MarkerOptions markerOptions) {
 BitmapDescriptor markerDescriptor = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA);
 markerOptions.icon(markerDescriptor);
 }
 @Override
 protected void onClusterItemRendered(MyItem clusterItem, Marker marker) {
 super.onClusterItemRendered(clusterItem, marker);
 }
 @Override
 protected void onBeforeClusterRendered(Cluster<MyItem> cluster, MarkerOptions markerOptions){
 final Drawable clusterIcon = getResources().getDrawable(R.drawable.ic_lens_black_24dp);
 clusterIcon.setColorFilter(getResources().getColor(android.R.color.holo_orange_light), PorterDuff.Mode.SRC_ATOP);
 mClusterIconGenerator.setBackground(clusterIcon);
 //modify padding for one or two digit numbers
 if (cluster.getSize() < 10) {
 mClusterIconGenerator.setContentPadding(40, 20, 0, 0);
 }
 else {
 mClusterIconGenerator.setContentPadding(30, 20, 0, 0);
 }
 Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
 markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
 }
}

Full class code:

public class MapsActivity extends AppCompatActivity
 implements ClusterManager.OnClusterItemInfoWindowClickListener<MyItem> {
 private ClusterManager<MyItem> mClusterManager;
 private MyItem clickedClusterItem;
 private GoogleMap mMap;
 @Override
 protected void onCreate(Bundle savedInstanceState) {
 super.onCreate(savedInstanceState);
 setContentView(R.layout.activity_maps);
 setUpMapIfNeeded();
 }
 @Override
 protected void onResume() {
 super.onResume();
 setUpMapIfNeeded();
 }
 private void setUpMapIfNeeded() {
 // Do a null check to confirm that we have not already instantiated the map.
 if (mMap == null) {
 // Try to obtain the map from the SupportMapFragment.
 mMap = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map))
 .getMap();
 // Check if we were successful in obtaining the map.
 if (mMap != null) {
 setUpMap();
 }
 }
 }
 private void setUpMap() {
 mMap.getUiSettings().setMapToolbarEnabled(true);
 mMap.getUiSettings().setZoomControlsEnabled(true);
 mMap.setMyLocationEnabled(true);
 mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
 mClusterManager = new ClusterManager<>(this, mMap);
 mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(37.779977,-122.413742), 10));
 mMap.setOnCameraChangeListener(mClusterManager);
 mMap.setOnMarkerClickListener(mClusterManager);
 mClusterManager.setRenderer(new MyClusterRenderer(this, mMap,
 mClusterManager));
 mMap.setInfoWindowAdapter(mClusterManager.getMarkerManager());
 mMap.setOnInfoWindowClickListener(mClusterManager); //added
 mClusterManager.setOnClusterItemInfoWindowClickListener(this); //added
 mClusterManager
 .setOnClusterItemClickListener(new ClusterManager.OnClusterItemClickListener<MyItem>() {
 @Override
 public boolean onClusterItemClick(MyItem item) {
 clickedClusterItem = item;
 return false;
 }
 });
 addItems();
 mClusterManager.getMarkerCollection().setOnInfoWindowAdapter(
 new MyCustomAdapterForItems());
 }
 private void addItems() {
 double latitude = 37.779977;
 double longitude = -122.413742;
 for (int i = 0; i < 10; i++) {
 double offset = i / 60d;
 double lat = latitude + offset;
 double lng = longitude + offset;
 MyItem offsetItem = new MyItem(lat, lng, "title " + i+1, "snippet " + i+1);
 mClusterManager.addItem(offsetItem);
 }
 }
 //added with edit
 @Override
 public void onClusterItemInfoWindowClick(MyItem myItem) {
 //Cluster item InfoWindow clicked, set title as action
 Intent i = new Intent(this, OtherActivity.class);
 i.setAction(myItem.getTitle());
 startActivity(i);
 //You may want to do different things for each InfoWindow:
 if (myItem.getTitle().equals("some title")){
 //do something specific to this InfoWindow....
 }
 }
 public class MyCustomAdapterForItems implements GoogleMap.InfoWindowAdapter {
 private final View myContentsView;
 MyCustomAdapterForItems() {
 myContentsView = getLayoutInflater().inflate(
 R.layout.info_window, null);
 }
 @Override
 public View getInfoWindow(Marker marker) {
 TextView tvTitle = ((TextView) myContentsView
 .findViewById(R.id.txtTitle));
 TextView tvSnippet = ((TextView) myContentsView
 .findViewById(R.id.txtSnippet));
 tvTitle.setText(clickedClusterItem.getTitle());
 tvSnippet.setText(clickedClusterItem.getSnippet());
 return myContentsView;
 }
 @Override
 public View getInfoContents(Marker marker) {
 return null;
 }
 }
 public class MyClusterRenderer extends DefaultClusterRenderer<MyItem> {
 private final IconGenerator mClusterIconGenerator = new IconGenerator(getApplicationContext());
 public MyClusterRenderer(Context context, GoogleMap map,
 ClusterManager<MyItem> clusterManager) {
 super(context, map, clusterManager);
 }
 @Override
 protected void onBeforeClusterItemRendered(MyItem item,
 MarkerOptions markerOptions) {
 BitmapDescriptor markerDescriptor = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA);
 markerOptions.icon(markerDescriptor);
 }
 @Override
 protected void onClusterItemRendered(MyItem clusterItem, Marker marker) {
 super.onClusterItemRendered(clusterItem, marker);
 }
 @Override
 protected void onBeforeClusterRendered(Cluster<MyItem> cluster, MarkerOptions markerOptions){
 final Drawable clusterIcon = getResources().getDrawable(R.drawable.ic_lens_black_24dp);
 clusterIcon.setColorFilter(getResources().getColor(android.R.color.holo_orange_light), PorterDuff.Mode.SRC_ATOP);
 mClusterIconGenerator.setBackground(clusterIcon);
 //modify padding for one or two digit numbers
 if (cluster.getSize() < 10) {
 mClusterIconGenerator.setContentPadding(40, 20, 0, 0);
 }
 else {
 mClusterIconGenerator.setContentPadding(30, 20, 0, 0);
 }
 Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
 markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
 }
 }
}

Result:

Initial app launch:

No clustering

Zooming out, some clustering:

initial clustering

Zooming out again, all Markers clustered:

all Markers clustered

answered Jun 22, 2015 at 5:13
Sign up to request clarification or add additional context in comments.

4 Comments

Thank you. Would I be able to have different colors like blue for 5 and red for 10?
@qwertz Yes, you should be able to use cluster.getSize() and change the color of the cluster accordingly.
could you please take alook at this quedtion? Can't seem to find any solution :( stackoverflow.com/questions/30963602/…
Good, but not enough. The way you "center" the text in cluster is not a good aproach. It's not really centered. @sWift answer is definetely better.
28

We can Override getColor in CustomClusterRenderer.

public class CustomClusterRenderer extends DefaultClusterRenderer<CustomClusterItem> {
@Override
 protected int getColor(int clusterSize) {
 return Color.parseColor("#567238");
 }
}
answered Aug 16, 2016 at 12:20

3 Comments

how to use it? When I'm just adding this code to my project it shows me error - there is no default constructor available in 'com.google.maps.clustering.view. DefaultClusterRenderer'
Best solutions are usually simple.
this is working, any way to change the background and foreground color too?
13

I took some methods of superclass and partially remade them. Now i have beautiful standard clusters with my own colors.

public class CustomClusterRenderer extends DefaultClusterRenderer<GoogleMapMarker> {
private final IconGenerator mIconGenerator;
private ShapeDrawable mColoredCircleBackground;
private SparseArray<BitmapDescriptor> mIcons = new SparseArray();
private final float mDensity;
private Context mContext;
public CustomClusterRenderer(Context context, GoogleMap map,
 ClusterManager<GoogleMapMarker> clusterManager) {
 super(context, map, clusterManager);
 this.mContext = context;
 this.mDensity = context.getResources().getDisplayMetrics().density;
 this.mIconGenerator = new IconGenerator(context);
 this.mIconGenerator.setContentView(this.makeSquareTextView(context));
 this.mIconGenerator.setTextAppearance(
 com.google.maps.android.R.style.ClusterIcon_TextAppearance);
 this.mIconGenerator.setBackground(this.makeClusterBackground());
}
@Override
protected void onBeforeClusterRendered(Cluster<GoogleMapMarker> cluster,
 MarkerOptions markerOptions) {
 // Main color
 int clusterColor = mContext.getResources().getColor(R.color.colorPrimary);
 int bucket = this.getBucket(cluster);
 BitmapDescriptor descriptor = this.mIcons.get(bucket);
 if(descriptor == null) {
 this.mColoredCircleBackground.getPaint().setColor(clusterColor);
 descriptor = BitmapDescriptorFactory.fromBitmap(
 this.mIconGenerator.makeIcon(this.getClusterText(bucket)));
 this.mIcons.put(bucket, descriptor);
 }
 markerOptions.icon(descriptor);
}
private SquareTextView makeSquareTextView(Context context) {
 SquareTextView squareTextView = new SquareTextView(context);
 ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(-2, -2);
 squareTextView.setLayoutParams(layoutParams);
 squareTextView.setId(com.google.maps.android.R.id.text);
 int twelveDpi = (int)(12.0F * this.mDensity);
 squareTextView.setPadding(twelveDpi, twelveDpi, twelveDpi, twelveDpi);
 return squareTextView;
}
private LayerDrawable makeClusterBackground() {
 // Outline color
 int clusterOutlineColor = mContext.getResources().getColor(R.color.colorWhite);
 this.mColoredCircleBackground = new ShapeDrawable(new OvalShape());
 ShapeDrawable outline = new ShapeDrawable(new OvalShape());
 outline.getPaint().setColor(clusterOutlineColor);
 LayerDrawable background = new LayerDrawable(
 new Drawable[]{outline, this.mColoredCircleBackground});
 int strokeWidth = (int)(this.mDensity * 3.0F);
 background.setLayerInset(1, strokeWidth, strokeWidth, strokeWidth, strokeWidth);
 return background;
}

And then set renderer to Cluster Manager

mClusterManager = new ClusterManager<>(context, mGoogleMap);
mClusterManager.setRenderer(new CustomClusterRenderer(context, mGoogleMap, mClusterManager));
answered Aug 4, 2015 at 17:50

3 Comments

Thanks, I will check it out
mColoredCircleBackground.getPaint().setColor(clusterColor); not working on release code. I mean signed apk. Any other way for this?
Now this.mIcons.get(bucket); won't work, mIcons is declared as private in superclass
6

Nice custom renderer with centered text and different sizes of clusters:

 public class MyClusterRenderer extends DefaultClusterRenderer<Station> {
 private final IconGenerator mClusterIconGeneratorBig = new IconGenerator(getCtx());
 private final IconGenerator mClusterIconGeneratorMed = new IconGenerator(getCtx());
 private final IconGenerator mClusterIconGeneratorSml = new IconGenerator(getCtx());
 final Drawable clusterIconBig = getResources().getDrawable(R.drawable.marker1);
 final Drawable clusterIconMed = getResources().getDrawable(R.drawable.marker2);
 final Drawable clusterIconSml = getResources().getDrawable(R.drawable.marker3);
 public MyClusterRenderer(Context context, GoogleMap map,
 ClusterManager<Station> clusterManager) {
 super(context, map, clusterManager);
 setupIconGen(mClusterIconGeneratorBig, clusterIconBig, context);
 setupIconGen(mClusterIconGeneratorMed, clusterIconMed, context);
 setupIconGen(mClusterIconGeneratorSml, clusterIconSml, context);
 }
 private void setupIconGen(IconGenerator generator, Drawable drawable, Context context) {
 TextView textView = new TextView(context);
 textView.setTextAppearance(context, R.style.BubbleText);
 textView.setTypeface(App.FONTS[2]);
 textView.setId(com.google.maps.android.R.id.amu_text);
 textView.setGravity(android.view.Gravity.CENTER);
 textView.setLayoutParams(new FrameLayout.LayoutParams(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()));
 generator.setContentView(textView);
 generator.setBackground(drawable);
 }
 @Override
 protected void onBeforeClusterItemRendered(Station item, MarkerOptions markerOptions) {
 BitmapDescriptor markerDescriptor = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA);
 markerOptions.icon(markerDescriptor);
 }
 @Override
 protected void onClusterItemRendered(Station clusterItem, Marker marker) {
 super.onClusterItemRendered(clusterItem, marker);
 }
 @Override
 protected void onBeforeClusterRendered(Cluster<Station> cluster, MarkerOptions markerOptions) {
 if (cluster.getSize() > 20) {
 Bitmap icon = mClusterIconGeneratorBig.makeIcon(String.valueOf(cluster.getSize()));
 markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
 } else if (cluster.getSize() > 10) {
 Bitmap icon = mClusterIconGeneratorMed.makeIcon(String.valueOf(cluster.getSize()));
 markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
 } else {
 Bitmap icon = mClusterIconGeneratorSml.makeIcon(String.valueOf(cluster.getSize()));
 markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
 }
 }
 @Override
 protected boolean shouldRenderAsCluster(Cluster cluster) {
 return cluster.getSize() > 5;
 }
}

enter image description here

answered Jan 27, 2018 at 8:51

1 Comment

And where do you set the text?
4

Go to DefaultClusterRenderer (package com.google.maps.android.clustering.view;), and change the getColor() method to this:

private int getColor(int clusterSize) {
 // custom color
 double _logClusterSize; // log
 final int _maxRed = Integer.parseInt("ff", 16);
// Log.v("kai", String.valueOf(_maxRed));
 final int _minRed = Integer.parseInt("e6", 16);
 final int _maxGreen = Integer.parseInt("a2", 16);
 final int _minGreen = Integer.parseInt("47", 16);
 final int _maxBlue = Integer.parseInt("93", 16);
 final int _minBlue = Integer.parseInt("2d", 16);
 final double _maxLogClusterSize = 10;
 double _step = (_maxRed - _minRed) / _maxLogClusterSize;
 _logClusterSize = Math.log(clusterSize);
 if(_logClusterSize > 10) _logClusterSize = 10;
 int _red = _maxRed - (int) (_step * _logClusterSize);
 int _green = _maxGreen - (int) (_step * _logClusterSize);
 int _blue = _maxBlue - (int) (_step * _logClusterSize);
 return Color.rgb(_red, _green, _blue);
// 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
// });
 }

This will change the Cluster color to pink, in the range of the color defined by min(max) red(green, blue). Hope that help!

answered Aug 11, 2015 at 16:58

2 Comments

This is by far the best answer.
I don't think that changing the source code of a library is of much help. It will make updates of the libraries error-prone.
1

Sadly, overriding getColor doesn't work for me. But this looks enough to change the marker color (and something else):

class ClusterItemRenderer(
 context: Context, map: GoogleMap,
 clusterManager: ClusterManager<ClusterMarker>
) : DefaultClusterRenderer<ClusterMarker>(context, map, clusterManager) {
 override fun onBeforeClusterItemRendered(item: ClusterMarker, markerOptions: MarkerOptions) {
 val markerDescriptor = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA)
 markerOptions.icon(markerDescriptor)
 }
}

It's also possible to add updates according to the recommendations:

If you're using custom clustering (i.e, if you're extending DefaultClusterRenderer), you must override two additional methods in v1:

  • onClusterItemUpdated() - should be the same* as your onBeforeClusterItemRendered() method
  • onClusterUpdated() - should be the same* as your onBeforeClusterRendered() method

*Note that these methods can't be identical, as you need to use a Marker instead of MarkerOptions

override fun onClusterItemUpdated(item: ClusterMarker, marker: Marker) {
 val markerDescriptor = BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_MAGENTA)
 marker.setIcon(markerDescriptor)
}
answered Jun 20, 2020 at 11:38

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.