12

I'm using the android clustering utility on a map and succesffuly implemented an onclick listener with mClusterManager.setOnClusterItemClickListener() for all the markers handled by the clustering library.

Because I also want some markers to always be unclustered, I also add some markers without using the mClusterManager but directly using the map's mMap.addMarker(), this way I'm sure that they are never clustered on the map.

My problem is that I cannot intercept clicks on those markers (the always unclustered ones) because I already used mMap.setOnMarkerClickListener(mClusterManager) to handle the clicked clusters' markers.

Is there a way to listen to the clicked clustered markers AND the clicked markers not handled by the clustering library ?

Or is there a way to specify the cluster manager to never cluster some markers ? In this case I won't have to handle those different click listener since all markers would be shown using the clustering utility.

Thank you

NasaGeek
  • 2,138
  • 1
  • 15
  • 19
tio oit
  • 193
  • 1
  • 1
  • 10

4 Answers4

19

You can create a new MarkerManager that you pass into the ClusterManager constructor. Then make a new Marker collection using MarkerManager#newCollection and add your normal Markers to the map using the MarkerManager.Collection#addMarker method.

Then, instead of calling mMap.setOnMarkerClickListener(mClusterManager), call mMap.setOnMarkerClickListener(mMarkerManager), and it will handle forwarding your Marker click events to the proper listeners. You'll also need to set up your onMarkerClick listener for normal Markers with the MarkerManager.Collection#setOnMarkerClickListener(GoogleMap.OnMarkerClickListener markerClickListener) function.

I recommend looking over the source of the MarkerManager and ClusterManager classes to get a better idea of how the classes interact.

NasaGeek
  • 2,138
  • 1
  • 15
  • 19
  • 3
    Perfect it's working, thx. Reading the MarkerManager link you gave really helped me, it's a good recommandation. – tio oit Oct 01 '15 at 17:09
  • @NasaGeek After adding markers with `MarkerManager`, cluster is not working! Can you please provide any snippet? – Maddy Dec 23 '19 at 05:56
8

One more way to receive the click event for Marker is using OnClusterItemClickListener interface.

Call mClusterManager.setOnClusterItemClickListener(this); and make your class implement OnClusterItemClickListener

Then inside the onClusterItemClick method, you will get the ClusterItem which is the marker that was clicked,

@Override
public boolean onClusterItemClick(ClusterItem clusterItem) {

    Toast.makeText(getActivity(), "Latitude " + clusterItem.getPosition().latitude, Toast.LENGTH_LONG).show();

    return true;
}
Atul O Holic
  • 6,692
  • 4
  • 39
  • 74
  • 1
    This is simpler. – 最白目 Jul 13 '17 at 09:44
  • 1
    But it doesn't have further attributes if you have a class that extended from `ClusterItem`. For example `Icon`, `Id`, ... . – Dr.jacky Aug 23 '17 at 11:03
  • @Mr.Hyde maybe you can trick the system, if you have a database the Lat and Lng won't change and you can reverse the data. I'd implement that in my project. Thanks @Atul! – Federico Navarrete Nov 02 '17 at 08:56
  • @Mr.Hyde The item that is passed into onClusterItemClick should still retain it's original type, so you could be able to cast it to the correct object to retain the fields. i.e. MyObject mine = (MyObject) clusterItem; – Zurvæ Apr 06 '18 at 16:32
0

Here you can see my code. The example is complete with all you need.

public class HomeFragment extends Fragment implements OnMapReadyCallback, ClusterManager.OnClusterClickListener<MarkerClusterItem> {

    ...

    @Override
    public void onMapReady(GoogleMap googleMap) {
        // Add a marker in Sydney, Australia,
        this.googleMap = googleMap;
        this.googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);


        setUpClusterManager();

        ...
    }

    private void setUpClusterManager(){
        // cluster
        clusterManager = new ClusterManager<MarkerClusterItem>(getActivity(), this.googleMap);
        clusterManager.setAnimation(false);
        MarkerClusterRenderer clusterRenderer = new MarkerClusterRenderer(getActivity(), googleMap, clusterManager);
        clusterManager.setRenderer(clusterRenderer);
        // marker clic
        clusterManager.setOnClusterClickListener(this);
    }

    @Override
    public boolean onClusterClick(Cluster<MarkerClusterItem> cluster) {
    if (cluster == null) return false;
        // do your stuff here 
        return true;
    }


 }
Zhar
  • 3,330
  • 2
  • 24
  • 25
0

Simple solution

Just got the same problem. The solution is simple: call ClusterManager listeners from your own event listeners. Look at the concept:

private OnMapReadyCallback listenerMapReady = (googleMap) ->
{
    googleMap.setOnCameraIdleListener(() -> {
        mClusterManager.onCameraIdle(); //call cluster manager listener
        //....your code for setOnCameraIdleListener event
    });

    googleMap.setOnMarkerClickListener(marker -> {
        if (marker != null && ... check if marker is NOT from cluster manager ....) {
            //......your code for unclustered markers, added directly to the map
            return false;
        }
        else
            return mClusterManager != null && mClusterManager.onMarkerClick(marker); // let cluster manager do his job
    });
}

To ensure that cluster manager internals will not intercept onMarkerClick listener, lets reset listener every time after cluster manager did his stuff. I've put it right into OnCameraIdle event:

public class FragmentRoutes extends Fragment {

private MapView mMapView;
private GoogleMap mMap;


@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View rootView = inflater.inflate(R.layout.fragment_routes, container, false);

    mMapView = rootView.findViewById(R.id.ac_main_map); // map rendering holder
    if (mMapView != null)
        mMapView.onCreate(savedInstanceState);
    //....... other onCreate mess
}


@Override
public void onResume() {
    super.onResume();
    if (mMapView != null) {
        mMapView.onResume();
        mMapView.getMapAsync(listenerMapReady);
    }
}


// on marker click listener for the map
private GoogleMap.OnMarkerClickListener listenerMarkerClick = marker -> {
    //markers which tag is TransportItem were added directly to map, I will take care of them
    if (marker != null && marker.getTag() instanceof TransportItem) {
        //......do my tasks for clicked transport marker
        return false;
    }
    else {
       // other markers are cluster manager markers, process as usual
       return mClusterManager != null && mClusterManager.onMarkerClick(marker);
    }
}

// listener for map ready event
private OnMapReadyCallback listenerMapReady = (googleMap) ->
{
    if (getContext() == null)
        return;

    mMap = googleMap;

    mMap.setOnCameraIdleListener(() -> {
        mClusterManager.onCameraIdle();

        //....your code for setOnCameraIdleListener event

        // now reset marker click listener
        mMap.setOnMarkerClickListener(listenerMarkerClick); //reset marker click listener
    });
}


}

Thats all! Happy androiding :)

Alex Gata
  • 31
  • 1
  • 4