Android Maps Utils Clustering Show Infowindow

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:

Sample Image

Zooming out, starts Clustering:

Sample Image

Zooming out again, more Clustering:

Sample Image

Then, zooming in, and clicking on an individual Marker:

Sample Image

Then clicking on another Marker:

Sample Image

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:

Sample Image

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



Leave a reply



Submit