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.
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:
Zooming out, some clustering:
Zooming out again, all Markers clustered:
We can Override getColor in CustomClusterRenderer.
public class CustomClusterRenderer extends DefaultClusterRenderer<CustomClusterItem> {
#Override
protected int getColor(int clusterSize) {
return Color.parseColor("#567238");
}
}
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));
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;
}
}
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!
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)
}
Related
can i add a Map style with a marker cluster in android? because i try with this line:
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mMap.setMapType(GoogleMap.MAP_TYPE_NORMAL);
MapStyleOptions style = loadRawResourceStyle(Mapa.this, R.raw.estilo_map);
mMap.setMapStyle(style);
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(-33.502482, -70.573841), 8));
mMap.getUiSettings().setMapToolbarEnabled(false);
mMap.getUiSettings().setMyLocationButtonEnabled(true);
mMap.getUiSettings().setZoomControlsEnabled(false);
if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
mMap.setMyLocationEnabled(true);
mMap.setMinZoomPreference(10);
mMap.setMaxZoomPreference(20);
mMap.setLatLngBoundsForCameraTarget(new LatLngBounds(new LatLng(-33.660048, -70.801025),new LatLng(-33.271212, -70.471835)));
mClusterManager = new ClusterManager<>(Mapa.this, mMap);
RenderClusterInfoWindow renderer = new RenderClusterInfoWindow(Mapa.this, mMap, mClusterManager);
mClusterManager.setRenderer(renderer);
mMap.setOnCameraIdleListener(mClusterManager);
mMap.setOnMarkerClickListener(mClusterManager);
mMap.setOnInfoWindowClickListener(mClusterManager);
clusterMarcadores();
mClusterManager.setOnClusterItemClickListener(new ClusterManager.OnClusterItemClickListener<FormatoMarcadores>() {
#Override
public boolean onClusterItemClick(FormatoMarcadores formatoMarcadores) {//abrir valorar y reportar aqui
Log.d("ciclovia", formatoMarcadores.getTitle());
d.ruta(formatoMarcadores.getTitle(), mMap);
markerClick(formatoMarcadores.getTitle(),formatoMarcadores.getPosition());
return false;
}
});
mClusterManager.setOnClusterClickListener(new ClusterManager.OnClusterClickListener<FormatoMarcadores>() {
#Override
public boolean onClusterClick(Cluster<FormatoMarcadores> cluster) {
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom(cluster.getPosition(), (float) Math.floor(mMap.getCameraPosition().zoom + 2)));
return true;
}
});
mClusterManager.cluster();
mMap.setOnMapClickListener(new GoogleMap.OnMapClickListener() {
#Override
public void onMapClick(LatLng latLng) {
d.quitarPolyline();
if (bottomSheet.isHideable()){
bottomSheet.setState(BottomSheetBehavior.STATE_HIDDEN);
}
}
});
}
and i have this: link to picture
maps works perfectly without style, and if i use the mMap.setMapStyle(style); as boolean, i get the style is applied correctly
Create you map theme from here
Download your style.json -> copy in raw resorce file
mGoogleMap.setMapStyle(
MapStyleOptions.loadRawResourceStyle(
this, R.raw.style_json));
try this code:
public class CustomMarkerClusteringDemoActivity extends BaseDemoActivity implements ClusterManager.OnClusterClickListener<Person>, ClusterManager.OnClusterInfoWindowClickListener<Person>, ClusterManager.OnClusterItemClickListener<Person>, ClusterManager.OnClusterItemInfoWindowClickListener<Person> {
private ClusterManager<Person> mClusterManager;
private Random mRandom = new Random(1984);
/**
* Draws profile photos inside markers (using IconGenerator).
* When there are multiple people in the cluster, draw multiple photos (using MultiDrawable).
*/
private class PersonRenderer extends DefaultClusterRenderer<Person> {
private final IconGenerator mIconGenerator = new IconGenerator(getApplicationContext());
private final IconGenerator mClusterIconGenerator = new IconGenerator(getApplicationContext());
private final ImageView mImageView;
private final ImageView mClusterImageView;
private final int mDimension;
public PersonRenderer() {
super(getApplicationContext(), getMap(), mClusterManager);
View multiProfile = getLayoutInflater().inflate(R.layout.multi_profile, null);
mClusterIconGenerator.setContentView(multiProfile);
mClusterImageView = (ImageView) multiProfile.findViewById(R.id.image);
mImageView = new ImageView(getApplicationContext());
mDimension = (int) getResources().getDimension(R.dimen.custom_profile_image);
mImageView.setLayoutParams(new ViewGroup.LayoutParams(mDimension, mDimension));
int padding = (int) getResources().getDimension(R.dimen.custom_profile_padding);
mImageView.setPadding(padding, padding, padding, padding);
mIconGenerator.setContentView(mImageView);
}
#Override
protected void onBeforeClusterItemRendered(Person person, MarkerOptions markerOptions) {
// Draw a single person.
// Set the info window to show their name.
mImageView.setImageResource(person.profilePhoto);
Bitmap icon = mIconGenerator.makeIcon();
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon)).title(person.name);
}
#Override
protected void onBeforeClusterRendered(Cluster<Person> cluster, MarkerOptions markerOptions) {
// Draw multiple people.
// Note: this method runs on the UI thread. Don't spend too much time in here (like in this example).
List<Drawable> profilePhotos = new ArrayList<Drawable>(Math.min(4, cluster.getSize()));
int width = mDimension;
int height = mDimension;
for (Person p : cluster.getItems()) {
// Draw 4 at most.
if (profilePhotos.size() == 4) break;
Drawable drawable = getResources().getDrawable(p.profilePhoto);
drawable.setBounds(0, 0, width, height);
profilePhotos.add(drawable);
}
MultiDrawable multiDrawable = new MultiDrawable(profilePhotos);
multiDrawable.setBounds(0, 0, width, height);
mClusterImageView.setImageDrawable(multiDrawable);
Bitmap icon = mClusterIconGenerator.makeIcon(String.valueOf(cluster.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(icon));
}
#Override
protected boolean shouldRenderAsCluster(Cluster cluster) {
// Always render clusters.
return cluster.getSize() > 1;
}
}
#Override
public boolean onClusterClick(Cluster<Person> cluster) {
// Show a toast with some info when the cluster is clicked.
String firstName = cluster.getItems().iterator().next().name;
Toast.makeText(this, cluster.getSize() + " (including " + firstName + ")", Toast.LENGTH_SHORT).show();
// Zoom in the cluster. Need to create LatLngBounds and including all the cluster items
// inside of bounds, then animate to center of the bounds.
// Create the builder to collect all essential cluster items for the bounds.
LatLngBounds.Builder builder = LatLngBounds.builder();
for (ClusterItem item : cluster.getItems()) {
builder.include(item.getPosition());
}
// Get the LatLngBounds
final LatLngBounds bounds = builder.build();
// Animate camera to the bounds
try {
getMap().animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 100));
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
#Override
public void onClusterInfoWindowClick(Cluster<Person> cluster) {
// Does nothing, but you could go to a list of the users.
}
#Override
public boolean onClusterItemClick(Person item) {
// Does nothing, but you could go into the user's profile page, for example.
return false;
}
#Override
public void onClusterItemInfoWindowClick(Person item) {
// Does nothing, but you could go into the user's profile page, for example.
}
#Override
protected void startDemo() {
getMap().moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.503186, -0.126446), 9.5f));
mClusterManager = new ClusterManager<Person>(this, getMap());
mClusterManager.setRenderer(new PersonRenderer());
getMap().setOnCameraIdleListener(mClusterManager);
getMap().setOnMarkerClickListener(mClusterManager);
getMap().setOnInfoWindowClickListener(mClusterManager);
mClusterManager.setOnClusterClickListener(this);
mClusterManager.setOnClusterInfoWindowClickListener(this);
mClusterManager.setOnClusterItemClickListener(this);
mClusterManager.setOnClusterItemInfoWindowClickListener(this);
addItems();
mClusterManager.cluster();
}
private void addItems() {
// http://www.flickr.com/photos/sdasmarchives/5036248203/
mClusterManager.addItem(new Person(position(), "Walter", R.drawable.walter));
// http://www.flickr.com/photos/usnationalarchives/4726917149/
mClusterManager.addItem(new Person(position(), "Gran", R.drawable.gran));
// http://www.flickr.com/photos/nypl/3111525394/
mClusterManager.addItem(new Person(position(), "Ruth", R.drawable.ruth));
// http://www.flickr.com/photos/smithsonian/2887433330/
mClusterManager.addItem(new Person(position(), "Stefan", R.drawable.stefan));
// http://www.flickr.com/photos/library_of_congress/2179915182/
mClusterManager.addItem(new Person(position(), "Mechanic", R.drawable.mechanic));
// http://www.flickr.com/photos/nationalmediamuseum/7893552556/
mClusterManager.addItem(new Person(position(), "Yeats", R.drawable.yeats));
// http://www.flickr.com/photos/sdasmarchives/5036231225/
mClusterManager.addItem(new Person(position(), "John", R.drawable.john));
// http://www.flickr.com/photos/anmm_thecommons/7694202096/
mClusterManager.addItem(new Person(position(), "Trevor the Turtle", R.drawable.turtle));
// http://www.flickr.com/photos/usnationalarchives/4726892651/
mClusterManager.addItem(new Person(position(), "Teach", R.drawable.teacher));
}
private LatLng position() {
return new LatLng(random(51.6723432, 51.38494009999999), random(0.148271, -0.3514683));
}
private double random(double min, double max) {
return mRandom.nextDouble() * (max - min) + min;
}
}
please check my code it is working fine i think it helps you.
I try to cluster some points in my application map, It work prefect for first time and data clustered but when try to update the list again with new data not change happen and clusters still exist I try to use clearItems(), clear() map but no change happen the following is my code for it can any one help please.
Code for map
((SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map)).getMapAsync(new OnMapReadyCallback() {
#Override
public void onMapReady(final GoogleMap googleMap) {
mMap = googleMap;
LatLngBounds ADELAIDE = new LatLngBounds(
new LatLng(16.57946, 35.69014), new LatLng(31.67252, 50.20833));
mMap.setLatLngBoundsForCameraTarget(ADELAIDE);
final LatLng location = new LatLng(mDefaultLat, mDefaultLng);
mClusterManager = new ClusterManager<>(getContext(), mMap);
mClusterManager.setRenderer(new ClusterRenderer(getContext(), mMap, mClusterManager));
final CameraPosition[] mPreviousCameraPosition = {null};
mMap.setOnCameraIdleListener(new GoogleMap.OnCameraIdleListener() {
#Override
public void onCameraIdle() {
CameraPosition position = googleMap.getCameraPosition();
mDefaultLat = position.target.latitude;
mDefaultLng = position.target.longitude;
locationFromMap = true;
populate();
if (mPreviousCameraPosition[0] == null || mPreviousCameraPosition[0].zoom != position.zoom) {
mPreviousCameraPosition[0] = googleMap.getCameraPosition();
addItems();
}
}
});
mMap.setOnMapLoadedCallback(new GoogleMap.OnMapLoadedCallback() {
#Override
public void onMapLoaded() {
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(location, 16));
}
});
mMap.setOnMarkerClickListener(new GoogleMap.OnMarkerClickListener() {
#Override
public boolean onMarkerClick(Marker marker) {
try {
new OfficeDialog(mActivity, officeList.get(Integer.parseInt(marker.getSnippet()))).show();
} catch (Exception e) {
}
return true;
}
});
}
});
addItems function
private void addItems() {
if (!listAdded && officeList.size() > 0) {
mClusterManager.clearItems();
mClusterManager.cluster();
mClusterManager.addItems(officeList);
listAdded = true;
}
mClusterManager.cluster();
}
and my cluster manager custom render
public class ClusterRenderer extends DefaultClusterRenderer<Office> {
Context context;
private IconGenerator iconGenerator;
private float density;
public ClusterRenderer(Context context, GoogleMap map, ClusterManager<Office> clusterManager) {
super(context, map, clusterManager);
clusterManager.setRenderer(this);
this.context = context;
density = context.getResources().getDisplayMetrics().density;
}
#Override
protected void onBeforeClusterItemRendered(Office item, MarkerOptions markerOptions) {
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ejar_location_icon_copy));
}
#Override
protected void onBeforeClusterRendered(Cluster<Office> cluster, MarkerOptions markerOptions) {
if(iconGenerator == null) {
iconGenerator = new IconGenerator(context);
iconGenerator.setContentView(makeTextView(context));
}
iconGenerator.setBackground(makeBackground(false, cluster.getSize()));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(iconGenerator.makeIcon(String.valueOf(cluster.getSize()))));
}
#Override
protected void onClusterRendered(Cluster<Office> cluster, Marker marker) {
super.onClusterRendered(cluster, marker);
}
#Override
protected boolean shouldRenderAsCluster(Cluster<Office> cluster) {
return cluster.getSize() > 1;
}
private ShapeDrawable makeBackground(boolean isClicked, int size) {
ShapeDrawable background = new ShapeDrawable(new OvalShape());
int color = ContextCompat.getColor(context, R.color.colorPrimary);
if (size < 50) {
color = ContextCompat.getColor(context, R.color.colorPrimary);
} else if (size < 100) {
color = ContextCompat.getColor(context, R.color.cluster_50);
} else if (size < 200) {
color = ContextCompat.getColor(context, R.color.cluster_100);
} else if (size < 1000) {
color = ContextCompat.getColor(context, R.color.cluster_200);
} else color = ContextCompat.getColor(context, R.color.cluster_1000);
background.setColorFilter(color, PorterDuff.Mode.SRC_ATOP);
return background;
}
private SquareTextView makeTextView(Context context) {
SquareTextView squareTextView = new SquareTextView(context);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(-2, -2);
squareTextView.setLayoutParams(layoutParams);
squareTextView.setTextColor(ContextCompat.getColor(context, R.color.white));
squareTextView.setId(com.google.maps.android.R.id.text);
int twelveDpi = (int) (12.0F * density);
squareTextView.setPadding(twelveDpi, twelveDpi, twelveDpi, twelveDpi);
return squareTextView;
}
public IconGenerator getIconGenerator(boolean isClicked) {
iconGenerator.setBackground(makeBackground(isClicked, 0));
return iconGenerator;
}
}
After debug and search about this issue I didn't found any reason about it so after check my web service I found the issue on it.
Web service return static locations for my objects so the clusters not changed after fix it the application works correctly without any issues.
When I have a cluster 17, 18, it shows "10+", "20+". How can I display the exact number?
Thanks.
public class MyClusterItemRenderer extends DefaultClusterRenderer<MyClusterItem> {
private final IconGenerator mIconGenerator;
private final ShapeDrawable mColoredCircleBackground;
private final float mDensity;
private SparseArray<BitmapDescriptor> mIcons = new SparseArray();
public MyClusterItemRenderer(Context context, GoogleMap map, ClusterManager<MyClusterItem> clusterManager) {
super(context, map, clusterManager);
mIconGenerator = new IconGenerator(context);
mColoredCircleBackground = new ShapeDrawable(new OvalShape());
mDensity = context.getResources().getDisplayMetrics().density;
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);
this.mIconGenerator.setContentView(squareTextView);
mIconGenerator.setTextAppearance(com.google.maps.android.R.style.ClusterIcon_TextAppearance);
ShapeDrawable outline = new ShapeDrawable(new OvalShape());
outline.getPaint().setColor(Color.parseColor("#FFFFFF"));
LayerDrawable background = new LayerDrawable(new Drawable[]{outline, this.mColoredCircleBackground});
int strokeWidth = (int) (this.mDensity * 3.0F);
background.setLayerInset(1, strokeWidth, strokeWidth, strokeWidth, strokeWidth);
mIconGenerator.setBackground(background);
}
#Override
protected void onBeforeClusterRendered(Cluster<MyClusterItem> cluster, MarkerOptions markerOptions) {
int bucket = this.getBucket(cluster);
BitmapDescriptor descriptor = (BitmapDescriptor)this.mIcons.get(bucket);
if(descriptor == null) {
this.mColoredCircleBackground.getPaint().setColor(this.getColor(bucket));
descriptor = BitmapDescriptorFactory.fromBitmap(this.mIconGenerator.makeIcon(String.valueOf(cluster.getSize())));
this.mIcons.put(bucket, descriptor);
}
markerOptions.icon(descriptor);
}
}
on activity:
clusterManager.setRenderer(new MyClusterItemRenderer(getApplicationContext(), mGoogleMap, clusterManager));
Google Map by default provide approximate number of clustered items. To achieve exact number of clustered item, just override these two methods in your custom cluster renderer class:
#Override
protected String getClusterText(int bucket) {
return String.valueOf(bucket);
}
#Override
protected int getBucket(Cluster<MyItem> cluster) {
return cluster.getSize();
}
The complete code snippet is :
public class MarkerClusterRenderer extends DefaultClusterRenderer<MyItem> {
MarkerClusterRenderer(Context context, GoogleMap map, ClusterManager<MyItem> clusterManager) {
super(context, map, clusterManager);
}
#Override
protected void onBeforeClusterItemRendered(MyItem item, MarkerOptions markerOptions) {
// use this to make your change to the marker option
// for the marker before it gets render on the map
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.ic_map_pins));
}
#Override
protected void onClusterItemRendered(MyItem item, Marker marker) {
//set marker title, tag, snippet, e.t.c here
}
#Override
protected String getClusterText(int bucket) {
return String.valueOf(bucket);
}
#Override
protected int getBucket(Cluster<MyItem> cluster) {
return cluster.getSize();
}
}
Here is the result :
Happy Coding :)
You can implement ClusterRenderer to customize the rendering of the clusters. DefaultClusterRenderer provides a good base to start from. By subclassing DefaultClusterRenderer, you can override the defaults.
For an in-depth example of customization, take a look at CustomMarkerClusteringDemoActivity in the demo app that ships with the utility library.
Source
This one is a bit old, but since it helped me find an easier solution I thought I post here.
Berdimurat has the right idea except the solution is simpler.
#Override
protected int getBucket(Cluster<FlagItemCluster> cluster) {
return cluster.getSize();
}
#Override
protected String getClusterText(int bucket) {
return String.valueOf(bucket);
}
Basically rather than using onBeforeClusterRendered, just alter where it gets it's data. This method also corrects that the number displayed isn't accurate because of what the getBucket logic contains.
I want to change the background of the cluster marker on click. I do this via
#Override
onClusterClick(Cluster<MyObject> cluster) {
Marker marker = renderer.getMarker(cluster);
marker.setIcon(....);
}
This works fine expect for one case: When I zoom in or out and the number of cluster markers doesn't change. For example, if I had a 15 cluster and a 5 cluster, then zoom a level in or out, the same two clusters remain. Clicking on one of these now renderer.getMarker(cluster) returns null. If they re-cluster after zooming, getMarker is not null.
My DefaultClusterRenderer is below. I checked the marker on onClusteredRendered and it's never null. Is this a bug in the DefaultClusterRenderer or should I do something else?
private class MyRenderer extends DefaultClusterRenderer<MyObject> {
private IconGenerator iconGenerator;
private float density;
public MyRenderer(Context context, GoogleMap map, ClusterManager<MyObject> clusterManager) {
super(context, map, clusterManager);
density = context.getResources().getDisplayMetrics().density;
}
#Override
protected void onBeforeClusterItemRendered(MyObject item, MarkerOptions markerOptions) {
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.drawable.my_pin));
}
#Override
protected void onBeforeClusterRendered(Cluster<MyObject> cluster, MarkerOptions markerOptions) {
if(iconGenerator == null) {
iconGenerator = new IconGenerator(getActivity());
iconGenerator.setContentView(makeTextView(getActivity()));
}
iconGenerator.setBackground(makeBackground(false));
markerOptions.icon(BitmapDescriptorFactory.fromBitmap(iconGenerator.makeIcon(String.valueOf(cluster.getSize()))));
}
#Override
protected void onClusterRendered(Cluster<MyObject> cluster, Marker marker) {
super.onClusterRendered(cluster, marker);
// Marker is never null here
}
#Override
protected boolean shouldRenderAsCluster(Cluster<MyObject> cluster) {
return cluster.getSize() > 1;
}
private ShapeDrawable makeBackground(boolean isClicked) {
ShapeDrawable background = new ShapeDrawable(new OvalShape());
background.setColorFilter(ContextCompat.getColor(getActivity(),
isClicked ? R.color.cluster_marker_clicked : R.color.cluster_marker_unclicked), PorterDuff.Mode.SRC_ATOP);
return background;
}
private SquareTextView makeTextView(Context context) {
SquareTextView squareTextView = new SquareTextView(context);
ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(-2, -2);
squareTextView.setLayoutParams(layoutParams);
squareTextView.setTextColor(ContextCompat.getColor(getActivity(), R.color.white));
squareTextView.setTypeface(Utils.getFontBold(getActivity()));
squareTextView.setId(com.google.maps.android.R.id.text);
int twelveDpi = (int) (12.0F * density);
squareTextView.setPadding(twelveDpi, twelveDpi, twelveDpi, twelveDpi);
return squareTextView;
}
public IconGenerator getIconGenerator(boolean isClicked) {
iconGenerator.setBackground(makeBackground(isClicked));
return iconGenerator;
}
}
Initializing the ClusterManager:
final ClusterManager<MyObject> mClusterManager = new ClusterManager<>(getActivity(), googleMap);
mClusterManager.addItems(items);
renderer = new CustomRenderer(getActivity(), googleMap, mClusterManager);
mClusterManager.setRenderer(renderer);
mClusterManager.cluster();
mClusterManager.setOnClusterItemClickListener(this);
googleMap.setOnMarkerClickListener(mClusterManager);
#antonio: This initialization is working for me:
public class MapsActivity extends FragmentActivity
implements ClusterManager.OnClusterClickListener<MyObject> {
// ...
private void setUpClusterer() {
googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(new LatLng(51.503186, -0.126446), 10));
mClusterManager = new ClusterManager<MyObject>(this, googleMap);
mClusterManager.setOnClusterClickListener(this);
renderer = new MyRenderer(this, googleMap, mClusterManager);
mClusterManager.setRenderer(renderer);
googleMap.setOnCameraChangeListener(mClusterManager);
googleMap.setOnMarkerClickListener(mClusterManager);
addItems();
}
private void addItems() {
// Set some lat/lng coordinates to start with.
double lat = 51.5145160;
double lng = -0.1270060;
// Add ten cluster items in close proximity, for purposes of this example.
for (int i = 0; i < 10; i++) {
double offset = i / 60d;
lat = lat + offset;
lng = lng + offset;
MyObject offsetItem = new MyObject(lat, lng);
mClusterManager.addItem(offsetItem);
}
}
#Override
public boolean onClusterClick(final Cluster<MyObject> cluster) {
Marker marker = renderer.getMarker(cluster);
marker.setIcon(BitmapDescriptorFactory.fromResource(R.drawable.my_newpin));
return true;
}
}
Couldn't get renderer.getMarker(cluster) to return a marker in that case above. A work around was to create:
Map <Cluster<MyObject>, Marker> clusterMarkerMap = new HashMap<>();
then add them on the DefaultClusterRenderer callback because the marker is never null there:
#Override
protected void onClusterRendered(Cluster<MyObject> cluster, Marker marker) {
super.onClusterRendered(cluster, marker);
clusterMarkerMap.put(cluster, marker);
}
Because the DefaultClusterManager clears them when the camera position changes, wipe the cluster marker map before it creates new ones:
googleMap.setOnCameraChangeListener(new GoogleMap.OnCameraChangeListener() {
#Override
public void onCameraChange(CameraPosition cameraPosition) {
// Clear the map here because the markers will be recreated
// when the manager is notified of a (zoom level) camera change
if(zoomLevelChanged)
clusterMarkerMap.clear();
mClusterManager.onCameraChange(cameraPosition);
}
});
Now I can get the marker successfully clusterMarkerMap.get(cluster)
I can't succeed to put a custom centered overlay item on my map. It always appears centered on bottom and middle. Here is my code:
------------My overlay item class-------------
public class EditionThumbOverlayItem extends OverlayItem {
public EditionThumbOverlayItem(GeoPoint aGeoPoint, Resources resources) {
super("", "", aGeoPoint);
Drawable pinThumbDrawable = resources.getDrawable(R.drawable.pin_thumb);
pinThumbDrawable.setBounds(pinThumbDrawable.getIntrinsicWidth() * (-2 / 5),
pinThumbDrawable.getIntrinsicHeight() * (-2 / 5), pinThumbDrawable.getIntrinsicWidth() * 3 / 5,
pinThumbDrawable.getIntrinsicHeight() * 3 / 5);
setMarker(pinThumbDrawable);
}
}
-------------my itemized overlay class-----------------
public class PinThumbOverlay extends ItemizedOverlay<OverlayItem> {
// Only one item can be set on this overlay
private OverlayItem mEditionThumb;
public PinThumbOverlay(Drawable pDefaultMarker, ResourceProxy pResourceProxy) {
super(pDefaultMarker, pResourceProxy);
}
#Override
public boolean onSnapToItem(int arg0, int arg1, Point arg2, IMapView arg3) {
return false;
}
#Override
protected OverlayItem createItem(int arg0) {
return mEditionThumb;
}
#Override
public int size() {
if (mEditionThumb == null) {
return 0;
} else {
return 1;
}
}
public void addOverlayItem(OverlayItem overlay) {
mEditionThumb = overlay;
populate();
}
public void removeOverlayItem() {
mEditionThumb = null;
populate();
}
}
-----------------itemized overlay creation----------------------
DefaultResourceProxyImpl defaultResouceProxyImpl = new DefaultResourceProxyImpl(getApplicationContext());
Drawable defaultPinEdition = getResources().getDrawable(R.drawable.pin_thumb);
mPinThumbOverlay = new PinThumbOverlay(defaultPinEdition, defaultResouceProxyImpl);
mMapView.getOverlays().clear();
mMapView.getOverlays().add(mPinThumbOverlay);
-------------------Overlay item creation-------------------------
EditionThumbOverlayItem editionThumb = new EditionThumbOverlayItem(new GeoPoint(mCurrentUserLocation),
getResources());
mPinThumbOverlay.addOverlayItem(editionThumb);
As a note: I use osmdroid map, not default google map api.
It seems that osmdroid overwrites any values you set with the drawble.setBounds() method in the onDrawItem method in ItemizedOverlay:
protected void onDrawItem(final Canvas canvas, final Item item, final Point curScreenCoords) {
final int state = (mDrawFocusedItem && (mFocusedItem == item) ? OverlayItem.ITEM_STATE_FOCUSED_MASK
: 0);
final Drawable marker = (item.getMarker(state) == null) ? getDefaultMarker(state) : item
.getMarker(state);
final HotspotPlace hotspot = item.getMarkerHotspot();
boundToHotspot(marker, hotspot);
// draw it
Overlay.drawAt(canvas, marker, curScreenCoords.x, curScreenCoords.y, false);
}
Or rather, the actual overwriting will be done in the boundToHotspot(marker,hotspot); method.
To overcome this set your hotspot on your OverlayItem instead of setting the bounds on the drawable.
Example:
GeoPoint point = new GeoPoint(currentLoc);
OverlayItem item = new OverlayItem("my location", "my loc", point);
item.setMarkerHotspot(HotspotPlace.BOTTOM_CENTER);
This should produce the result your looking for.
If you just want to put a marker in the map center, use this code snippet:
public class MyActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.main);
mapView = (MapView) findViewById(R.id.mapview);
mapView.setBuiltInZoomControls(true);
mapView.setMultiTouchControls(true);
mapView.setClickable(true);
mapView.setUseDataConnection(true);
mapView.getController().setZoom(MAP_DEFAULT_ZOOM);
mapView.setTileSource(TileSourceFactory.MAPNIK);
resourceProxy = new DefaultResourceProxyImpl(getApplicationContext());
GeoPoint location = getLocation()
ItemizedIconOverlay<OverlayItem> currentLocationOverlay;
Drawable marker = this.getResources().getDrawable(R.drawable.custom_marker);
OverlayItem myLocationOverlayItem = new OverlayItem("Here", "Current Position", location);
myLocationOverlayItem.setMarker(marker);
final ArrayList<OverlayItem> items = new ArrayList<OverlayItem>();
items.add(myLocationOverlayItem);
currentLocationOverlay = new ItemizedIconOverlay<OverlayItem>(items,
new ItemizedIconOverlay.OnItemGestureListener<OverlayItem>() {
public boolean onItemSingleTapUp(final int index, final OverlayItem item) {
return true;
}
public boolean onItemLongPress(final int index, final OverlayItem item) {
return true;
}
}, resourceProxy);
mapView.getOverlays().add(this.currentLocationOverlay);
mapView.getController().setCenter(location);
}