Android Maps Utils Clustering show InfoWindow
Here is a simplified and slightly modified solution based on this answer. Note that the linked answer implements an InfoWindow for both Markers and Clusters.
This solution only implements InfoWindows for Markers.
It's similar to how you would implement a custom InfoWindowAdapter for normal Markers with no Clustering, but with the additional requirement that you keep a reference to the currently selected Item so that you can get the Title and Snippet from it's MyItem
instance, since the Marker does not store the Title and Snippet as it usually does.
Note that since all of the data is stored in MyItem
references, it's much easier to extend the functionality to display as many data types as you want in the InfoWindow for each Marker.
First, the MyItem.java, which includes extra fields for Title and Snippet:
public class MyItem implements ClusterItem {
private final LatLng mPosition;
private final String mTitle;
private final String mSnippet;
public MyItem(double lat, double lng, String t, String s) {
mPosition = new LatLng(lat, lng);
mTitle = t;
mSnippet = s;
}
@Override
public LatLng getPosition() {
return mPosition;
}
public String getTitle(){
return mTitle;
}
public String getSnippet(){
return mSnippet;
}
}
Here is the full Activity class, which includes all of the functionality to support InfoWindows for each Marker added using the Cluster library:
Edit: Added support for handling click events on the InfoWindow, made the Activity implement OnClusterItemInfoWindowClickListener
and added the onClusterItemInfoWindowClick
callback.
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);
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;
}
}
}
info_window.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="20dp"
android:orientation="vertical"
android:background="#000000">
<TextView
android:id="@+id/txtTitle"
android:textColor="#D3649F"
android:textStyle="bold"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/txtSnippet"
android:textColor="#D3649F"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Result:
Initial launch:
Zooming out, starts Clustering:
Zooming out again, more Clustering:
Then, zooming in, and clicking on an individual Marker:
Then clicking on another Marker:
Edit: In order to show the "speech bubble" around the custom InfoWindow, use getInfoContents()
instead of getInfoWindow()
:
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) {
return null;
}
@Override
public View getInfoContents(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;
}
}
Result:
How to add info window for clustering marker in android?
MyItem
Class:
import com.google.android.gms.maps.model.LatLng;
import com.google.maps.android.clustering.ClusterItem;
public class MyItem implements ClusterItem {
private LatLng mPosition;
private String mLatitude = "";
private String mStoreLogo = "";
private String mLongitude = "";
@Override
public LatLng getPosition() {
return mPosition;
}
public void setPosition(LatLng mPosition) {
this.mPosition = mPosition;
}
public LatLng getmPosition() {
return mPosition;
}
public void setmPosition(LatLng mPosition) {
this.mPosition = mPosition;
}
public String getmLatitude() {
return mLatitude;
}
public void setmLatitude(String mLatitude) {
this.mLatitude = mLatitude;
}
public String getmLongitude() {
return mLongitude;
}
public void setmLongitude(String mLongitude) {
this.mLongitude = mLongitude;
}
public String getmStoreLogo() {
return mStoreLogo;
}
public void setmStoreLogo(String mStoreLogo) {
this.mStoreLogo = mStoreLogo;
}
}
Map
Activity Class:
public class Map extends FragmentActivity implements
ClusterManager.OnClusterClickListener<MyItem>,
ClusterManager.OnClusterInfoWindowClickListener<MyItem>,
ClusterManager.OnClusterItemClickListener<MyItem>,
ClusterManager.OnClusterItemInfoWindowClickListener<MyItem> {
private ClusterManager<MyItem> mClusterManager;
private Cluster<MyItem> clickedCluster;
private MyItem clickedClusterItem;
@SuppressWarnings("unchecked")
@Override
protected void onCreate(Bundle arg0) {
super.onCreate(arg0);
setContentView(R.layout.map_view);
try {
// Initializing Map from XML :
GooglePlayServicesUtil.isGooglePlayServicesAvailable(Map.this);
SupportMapFragment mapFrag = (SupportMapFragment) getSupportFragmentManager()
.findFragmentById(R.id.mMapView);
mMapView = mapFrag.getMap();
mMapView.setMapType(GoogleMap.MAP_TYPE_NORMAL);
mMapView.getUiSettings().setZoomControlsEnabled(true);
mMapView.getUiSettings().setCompassEnabled(true);
mMapView.getUiSettings().setMyLocationButtonEnabled(true);
mMapView.getUiSettings().setRotateGesturesEnabled(true);
mMapView.getUiSettings().setScrollGesturesEnabled(true);
mMapView.getUiSettings().setTiltGesturesEnabled(true);
mMapView.getUiSettings().setZoomGesturesEnabled(true);
mMapView.setMyLocationEnabled(true);
} catch (Exception e) {
mMapFrame.setVisibility(View.GONE);
Utils.displayToast("Your device doesn't support Google Map", Map.this);
}
// Creating cluster manager object.
mClusterManager = new ClusterManager<MyItem>(Map.this, mMapView);
mMapView.setOnCameraChangeListener(mClusterManager);
mClusterManager.setRenderer(new MyClusterRenderer(Map.this, mMapView,
mClusterManager));
mMapView.setOnInfoWindowClickListener(mClusterManager);
mMapView.setInfoWindowAdapter(mClusterManager.getMarkerManager());
mClusterManager.getClusterMarkerCollection().setOnInfoWindowAdapter(
new MyCustomAdapterForClusters());
mClusterManager.getMarkerCollection().setOnInfoWindowAdapter(
new MyCustomAdapterForItems());
mMapView.setOnMarkerClickListener(mClusterManager);
mClusterManager.setOnClusterClickListener(this);
mClusterManager.setOnClusterInfoWindowClickListener(this);
mClusterManager.setOnClusterItemClickListener(this);
mClusterManager.setOnClusterItemInfoWindowClickListener(this);
mClusterManager
.setOnClusterClickListener(new OnClusterClickListener<MyItem>() {
@Override
public boolean onClusterClick(Cluster<MyItem> cluster) {
clickedCluster = cluster;
return false;
}
});
mClusterManager
.setOnClusterItemClickListener(new OnClusterItemClickListener<MyItem>() {
@Override
public boolean onClusterItemClick(MyItem item) {
clickedClusterItem = item;
return false;
}
});
// Adding Objects to the Cluster.
mClusterManager.addItem(mItemData);
mMapView.animateCamera(CameraUpdateFactory
.newLatLngZoom(mLatLng, 7));
mClusterManager.cluster();
}
class MyClusterRenderer extends DefaultClusterRenderer<MyItem> {
public MyClusterRenderer(Context context, GoogleMap map,
ClusterManager<MyItem> clusterManager) {
super(context, map, clusterManager);
}
@Override
protected void onBeforeClusterItemRendered(MyItem item,
MarkerOptions markerOptions) {
super.onBeforeClusterItemRendered(item, markerOptions);
}
@Override
protected void onClusterItemRendered(MyItem clusterItem, Marker marker) {
super.onClusterItemRendered(clusterItem, marker);
}
}
// Custom adapter info view :
public class MyCustomAdapterForItems implements InfoWindowAdapter {
private final View myContentsView;
MyCustomAdapterForItems() {
myContentsView = getLayoutInflater().inflate(
R.layout.map_info_window_dialog, null);
}
@Override
public View getInfoContents(Marker marker) {
return null;
}
@Override
public View getInfoWindow(Marker marker) {
TextView tvTitle = ((TextView) myContentsView
.findViewById(R.id.txtHeader));
TextView tvSnippet = ((TextView) myContentsView
.findViewById(R.id.txtAddress));
tvTitle.setTypeface(mTyFaceKreonBold);
tvSnippet.setTypeface(mTyFaceKreonBold);
if (clickedClusterItem != null) {
tvTitle.setText(clickedClusterItem.getmStoreName());
tvSnippet.setText(clickedClusterItem.getmAddressOne());
}
return myContentsView;
}
}
// class for Main Clusters.
public class MyCustomAdapterForClusters implements InfoWindowAdapter {
private final View myContentsView;
MyCustomAdapterForClusters() {
myContentsView = getLayoutInflater().inflate(
R.layout.map_info_window_dialog, null);
}
@Override
public View getInfoContents(Marker marker) {
return null;
}
@Override
public View getInfoWindow(Marker marker) {
TextView tvTitle = ((TextView) myContentsView
.findViewById(R.id.txtHeader));
TextView tvSnippet = ((TextView) myContentsView
.findViewById(R.id.txtAddress));
tvSnippet.setVisibility(View.GONE);
tvTitle.setTypeface(mTyFaceKreonBold);
tvSnippet.setTypeface(mTyFaceKreonBold);
if (clickedCluster != null) {
tvTitle.setText(String
.valueOf(clickedCluster.getItems().size())
+ " more offers available");
}
return myContentsView;
}
}
@Override
public void onClusterItemInfoWindowClick(MyItem item) {
Intent intent = new Intent(Map.this,NextActivity.class);
intent.putExtra("mLatitude", item.getmLatitude());
intent.putExtra("mLongitude", item.getmLongitude());
startActivity(intent);
finish();
}
@Override
public boolean onClusterItemClick(MyItem item) {
// TODO Auto-generated method stub
return false;
}
@Override
public void onClusterInfoWindowClick(Cluster<MyItem> cluster) {
// TODO Auto-generated method stub
}
@Override
public boolean onClusterClick(Cluster<MyItem> cluster) {
// TODO Auto-generated method stub
return false;
}
}
The code is self explanatory. Please add the cluster library and google play services lib to your build path. Please let me know if you have any queries.
Showing custom InfoWindow for Android Maps Utility Library for Android
Yes, this can be done. ClusterManager
maintains two MarkerManager.Collections
:
- one for cluster markers, and
- one for individual item markers
You can set a custom InfoWindowAdapter
for each of these kinds of markers independently.
Implementation
First, install your ClusterManager's MarkerManager as the map's InfoWindowAdapter:
ClusterManager<MarkerItem> clusterMgr = new ClusterManager<MarkerItem>(context, map);
map.setInfoWindowAdapter(clusterMgr.getMarkerManager());
Next, install your custom InfoWindowAdapter
as the adapter for one or both of the marker collections:
clusterMgr.getClusterMarkerCollection().setOnInfoWindowAdapter(new MyCustomAdapterForClusters());
clusterMgr.getMarkerCollection().setOnInfoWindowAdapter(new MyCustomAdapterForItems());
The final piece is mapping the raw Marker
object that you'll receive in your custom InfoWindowAdapter's callback to the ClusterItem
object(s) that you added to the map in the first place. This can be achieved using the onClusterClick and onClusterItemClick listeners, as follows:
map.setOnMarkerClickListener(clusterMgr);
clusterMgr.setOnClusterClickListener(new OnClusterClickListener<MarkerItem>() {
@Override
public boolean onClusterClick(Cluster<MarkerItem> cluster) {
clickedCluster = cluster; // remember for use later in the Adapter
return false;
}
});
clusterMgr.setOnClusterItemClickListener(new OnClusterItemClickListener<MarkerItem>() {
@Override
public boolean onClusterItemClick(MarkerItem item) {
clickedClusterItem = item;
return false;
}
});
Now you have everything you need to assemble your custom InfoWindow content in your respective Adapters! For example:
class MyCustomAdapterForClusters implements InfoWindowAdapter {
@Override
public View getInfoContents(Marker marker) {
if (clickedCluster != null) {
for (MarkerItem item : clickedCluster.getItems()) {
// Extract data from each item in the cluster as needed
}
}
// build your custom view
// ...
return view;
}
}
Custom information in InfoWindow after pressing a marker using Map Cluster from Google Map utils
I did this in my app. Try this in your.
GoogleMap googleMap;
Double latitude;
Double longitude;
Marker mark;
MarkerOptions marker;
Bitmap bitmap;
String position, catName, activity;
ToggleButton listView, mapView;
Hashtable<Integer, String> markers;
ArrayList<HomeProperty> list = new ArrayList<HomeProperty>();
ArrayList<HomeProperty> newList;
ArrayList<HomeProperty> category = new ArrayList<HomeProperty>();
ArrayList<SearchProperty> searchList = new ArrayList<SearchProperty>();
ArrayList<HomeProperty> locationList = new ArrayList<HomeProperty>();
ProgressDialog dialog;
ImageLoader imageLoader;
@Override
protected void onCreate(Bundle savedInstanceState) {
// Introduction
super.onCreate(savedInstanceState);
setContentView(R.layout.map);
initilizeMap();
imageLoader = new ImageLoader(getApplicationContext());
markers = new Hashtable<Integer, String>();
listView = (ToggleButton) findViewById(R.id.listView);
mapView = (ToggleButton) findViewById(R.id.mapView);
listView.setOnCheckedChangeListener(this);
mapView.setOnCheckedChangeListener(this);
mapView.setChecked(true);
// setting the arraylist containing all data of events
ArrayData aData = (ArrayData) getIntent().getSerializableExtra("list");
position = getIntent().getStringExtra("position");
catName = getIntent().getStringExtra("catName");
activity = getIntent().getStringExtra("activity");
list = aData.getList();
ArrayData categoryData = (ArrayData) getIntent().getSerializableExtra(
"category");
category = categoryData.getList();
if (getIntent().hasExtra("searchData")) {
SearchData search = (SearchData) getIntent().getSerializableExtra(
"searchData");
searchList = search.getList();
} else {
searchList = new ArrayList<SearchProperty>();
}
newList = new ArrayList<HomeProperty>();
// for all categories
if (catName.equalsIgnoreCase("All")) {
newList.clear();
newList.addAll(list);
} else if (activity.equals("search")) {
newList.addAll(list);
}
// for single category
else {
for (int count = 0; count < list.size(); count++) {
if (list.get(count).getCategoryName().equals(catName)) {
newList.add(list.get(count));
}
}
}
// looping through All Transactions
for (int count = 0; count < newList.size(); count++) {
HomeProperty prop = new HomeProperty(newList.get(count).getLatitude(),
newList.get(count).getLongitude());
locationList.add(prop);
}
for (int count = 0; count < newList.size(); count++) {
marker = new MarkerOptions()
.position(
new LatLng(Double.parseDouble(list.get(count)
.getLatitude()), Double.parseDouble(newList
.get(count).getLongitude())))
.title(newList.get(count).getTitle())
.snippet(
new JSONMethods().SHORTIMAGEURL
+ newList.get(count).getImageDirectory()
+ "/thumb_" + newList.get(count).getImage());
googleMap.addMarker(marker);
CameraPosition cameraPosition = new CameraPosition.Builder()
.target(new LatLng(Double.parseDouble(newList.get(count)
.getLatitude()), Double.parseDouble(newList.get(count)
.getLongitude()))).zoom(12).build();
googleMap.animateCamera(CameraUpdateFactory
.newCameraPosition(cameraPosition));
googleMap.setInfoWindowAdapter(new CustomInfoWindowAdapter());
}
googleMap.getUiSettings().setZoomGesturesEnabled(true);
googleMap.getUiSettings().setCompassEnabled(true);
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
googleMap.getUiSettings().setRotateGesturesEnabled(true);
}
// set action on home button click in action bar
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar actions click
switch (item.getItemId()) {
// case R.id.action_settings:
// return true;
case android.R.id.home:
finish();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private class CustomInfoWindowAdapter implements InfoWindowAdapter {
private View view;
public CustomInfoWindowAdapter() {
view = getLayoutInflater().inflate(R.layout.custominfowindow, null);
}
@Override
public View getInfoContents(Marker mark) {
if (MapActivity.this.mark != null
&& MapActivity.this.mark.isInfoWindowShown()) {
MapActivity.this.mark.showInfoWindow();
}
return view;
}
@Override
public View getInfoWindow(final Marker mark) {
MapActivity.this.mark = mark;
final ImageView image = ((ImageView) view.findViewById(R.id.badge));
imageLoader.DisplayImage(mark.getSnippet(), image);
final String title = mark.getTitle();
final TextView titleUi = ((TextView) view.findViewById(R.id.title));
if (title != null) {
titleUi.setText(title);
} else {
titleUi.setText("");
}
return view;
}
}
Related Topics
Difference Between Google() and Maven { Url 'Https://Maven.Google.Com' }
Apache Httpclient Digest Authentication
Android Buildscript Repositories: Jcenter VS Mavencentral
Center Message in Android Dialog Box
Convert Normal Java Array or Arraylist to JSON Array in Android
Adding Header to All Request with Retrofit 2
How to Create Button Dynamically in Android
Theme.Appcompat.Light.Darkactionbar - No Resource Found
Read Data from SQLite Where Column Name Contains Spaces
Unable to Compile Jni Program Rjava
Simple Sso - Using Custom Authentication - Cas or Some Oauth or Openid Server
Why Would One Declare an Immutable Class Final in Java
Why Does a Try/Catch Block Create New Variable Scope
Calculate Distance in Meters When You Know Longitude and Latitude in Java