Search code examples
androidgoogle-mapsgoogle-maps-markersmarkerclustererandroid-maps-utils

Android Google maps markerClickListener for clusterItems


I'm trying to implement clusters for my googlemaps markers. Currently I have some custom marker colors with an API call that fires when you click on them. When those results are loaded, there will open a bottomsheet with the specific information of that marker.

I want to keep the same functionalities (custom markers/clickListeners/radius around markers), but add clusters when zoomed out. I've looked at different sources for help:

Android cluster and marker clicks

Android marker-clustering

MarkerManager.java

But i'm not sure how to implement the custom marker and listener for the clusters items. I'am able to get clusters with standard markers without clicklisteners. Here are some images for illustration:

This is my current situation (I want to cluster these markers). As you can see, the bottom sheet pops up when I click on a marker

Current sitution

This is what I'm currently able to do, but I want to combine it with the previous picture

Cluster map

Here is the important part of my code of my map Fragment, (The Point class does implement the ClusterItem interface):

private Map<Marker, Point> retailerInfo = new HashMap<>();

    private void markGeofencesOnMap() {
        new GeofenceAreasRequest().getAllAreas(new GeofenceAreasCallback() {
            @Override
            public void onAreasLoaded(List<Point> points) {
                for (final Point point : points) {
                    markerForGeofence(point);
                    drawRadius(point);
                }
            }
            @Override
            public void failedOnAreasLoaded(int message) {
                Snackbar.make(coordinatorLayout, R.string.failed_loading_areas, Snackbar.LENGTH_LONG).show();
            }
        });
    }

    private void markerForGeofence(Point point) {
        LatLng latLng = new LatLng(point.getLatitude(), point.getLongitude());
        MarkerOptions markerOptions = new MarkerOptions()
                .position(latLng)
                .flat(true)
                .title(point.getTitle())
                .icon(BitmapDescriptorFactory.defaultMarker(195));
//        markerManager.getCollection("markerCollection").addMarker(markerOptions);
//        markerManager.getCollection("markerCollection").setOnMarkerClickListener(this);
//        mClusterManager.addItem(point);
        geoFenceMarker = googleMap.addMarker(markerOptions);
        retailerInfo.put(geoFenceMarker, point);
    }

    private void drawRadius(Point point) {
        CircleOptions circleOptions = new CircleOptions()
                .center(geoFenceMarker.getPosition())
                .strokeColor(Color.argb(50, 70, 70, 70))
                .fillColor(Color.argb(100, 150, 150, 150))
                .radius(point.getRadius());
        googleMap.addCircle(circleOptions);
    }

    private void googleMapSettings() {
        int permissionCheck = ContextCompat.checkSelfPermission(getActivity(),
                Manifest.permission.ACCESS_FINE_LOCATION);
        if (permissionCheck == PackageManager.PERMISSION_GRANTED) {
            googleMap.setMyLocationEnabled(true);
        } else {
            SystemRequirementsChecker.checkWithDefaultDialogs(getActivity());
        }
        googleMap.getUiSettings().setZoomControlsEnabled(true);
        CameraPosition cameraPosition = new CameraPosition.Builder().target(getLastLocation()).zoom(12).build();
        googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
        googleMap.setOnMapClickListener(this);
//        markerManager = new MarkerManager(googleMap);
//        markerManager.newCollection("markerCollection");
//        mClusterManager = new ClusterManager<Point>(getActivity(), googleMap );//, markerManager);
//        googleMap.setOnMarkerClickListener(mClusterManager); //markerManager);
        googleMap.setOnCameraIdleListener(mClusterManager);
        googleMap.setOnMarkerClickListener(this);
    }

    @Override
    public boolean onMarkerClick(Marker marker) {
        if (retailerInfo != null) {
            String retailerId = retailerInfo.get(marker).getRetailer();
            new RetailersRequest().getRetailer(retailerId, new RetailersCallback() {
                @Override
                public void onRetailersLoad(List<Retailer> retailers) {
                    for (final Retailer retailer : retailers) {
                        mMapRetailerName.setText(retailer.getName());
                        mMapRetailerStreet.setText(retailer.getStreet());
                        mMapRetailerHouseNr.setText(retailer.getHousenumber());
                        mMapRetailerPostalCode.setText(retailer.getPostalCode());
                        mMapRetailerCity.setText(retailer.getCity());
                    }
                    bottomSheet.setVisibility(View.VISIBLE);
                    mBottomSheetBehavior.setPeekHeight(400);
                    mBottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED);
                }

                @Override
                public void failedOnRetailersLoaded(int code) {
                    Snackbar.make(coordinatorLayout, getString(R.string.failed_loading_retailers) + code, Snackbar.LENGTH_LONG).show();
                }
            });
            CameraPosition cameraPosition = new CameraPosition.Builder().target(marker.getPosition()).zoom(14).build();
            googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
        }
        return true;
    }

    @Override
    public void onMapClick(LatLng latLng) {
        bottomSheet.setVisibility(View.GONE);
    }

    @Override
    public void onMapReady(GoogleMap googleMap) {
        this.googleMap = googleMap;
        googleMapSettings();
        markGeofencesOnMap();
    }

I hope someone can help me out in the right direction. Thx!


Solution

  • Okay, after some more trail and error, I think I've got it sorted out for the most part. All I had to do was to attach a custom DefaultClusterRenderer to my clusterManager. In the mapfragment I could use onClusterItemClick for handling the marker clicks. The one problem I still have is that the circles are not all properly rendered when zooming in and out.

    private void markGeofencesOnMap() {
        new GeofenceAreasRequest().getAllAreas(new GeofenceAreasCallback() {
            @Override
            public void onAreasLoaded(List<Point> points) {
                for (final Point point : points) {
                    mClusterManager.addItem(point);
                }
            }
        });
    }
    
    private void googleMapSettings() {        
        mClusterManager = new ClusterManager<Point>(getActivity(), googleMap );
        // attach custom renderer behaviour
        mClusterManager.setRenderer(new OwnPointRendered(getActivity().getApplicationContext(), googleMap, mClusterManager)); 
        mClusterManager.setOnClusterItemClickListener(this);
        googleMap.setOnMarkerClickListener(mClusterManager);
        googleMap.setOnCameraIdleListener(mClusterManager);
    }
    
    @Override
    public boolean onClusterItemClick(ClusterItem clusterItem) {
        // cast ClusterItem to my Point class to handle marker clicks
        Point retailer = (Point) clusterItem;
        String retailerId = retailer.getRetailer();
        return true;
    }
    

    OwnPointRendered.class

    public class OwnPointRendered extends DefaultClusterRenderer<Point> {
    private final GoogleMap map;
    private List<Circle> circleList = new ArrayList<>();
    
    public OwnPointRendered(Context context, GoogleMap map,
                            ClusterManager<Point> clusterManager) {
        super(context, map, clusterManager);
        this.map = map;
    }
    
    @Override
    protected void onBeforeClusterItemRendered(Point item, MarkerOptions markerOptions) {
        markerOptions.flat(true);
        markerOptions.icon(BitmapDescriptorFactory.defaultMarker(195));
        drawRadius(item);
        super.onBeforeClusterItemRendered(item, markerOptions);
    }
    
    @Override
    protected void onClusterRendered(Cluster<Point> cluster, Marker marker) {
        super.onClusterRendered(cluster, marker);
        for (Circle circle : circleList) {
            circle.remove();
        }
        circleList.clear();
    }
    
    private void drawRadius(Point point) {
        CircleOptions circleOptions = new CircleOptions()
                .center(point.getPosition())
                .strokeColor(Color.argb(50, 70, 70, 70))
                .fillColor(Color.argb(100, 150, 150, 150))
                .radius(point.getRadius());
        Circle circle = map.addCircle(circleOptions);
        circleList.add(circle);
    }