How to change the clicked marker icon using android-maps-utils?
Asked Answered
L

3

8

In my Android project I am using the android-maps-utils library to apply clustering to a bunch of markers on a map view. Whenever a marker is clicked I get notified via onClusterItemClick so I can do some action.

public interface OnClusterItemClickListener<T extends ClusterItem> {
    public boolean onClusterItemClick(T item);
}

Now I would like to let the user know which marker has been clicked. The easies visual feedback would be to change the (color of the) marker icon. A icon can be set via the MarkerOptions object which can be access within onBeforeClusterItemRendered(T item, MarkerOptions markerOptions) such as here:

markerOptions.icon(
    BitmapDescriptorFactory.defaultMarker(
        BitmapDescriptorFactory.HUE_YELLOW));

If I would have access to the Marker object itself such as in onMarkerClick (Marker marker) I could change the icon via setIcon.

How can I change the clicked marker icon?


Related

Lilylilyan answered 15/6, 2015 at 10:57 Comment(0)
L
15

I noticed that the DefaultClusterRenderer provides methods to retrieve the Marker object associated with a ClusterItem. Since I use a custom renderer anyways I was able to access the desired Marker object as shown here:

mSelectedMarker = mCustomClusterItemRenderer.getMarker(mSelectedClusterItem);

This allows me to change the icon within onClusterItemClick():

private void updateSelectedMarker() {
    if (mSelectedMarker != null) {
        mSelectedMarker.setIcon(
                BitmapDescriptorFactory.defaultMarker(
                        BitmapDescriptorFactory.HUE_YELLOW));
    }
}
Lilylilyan answered 15/6, 2015 at 11:35 Comment(5)
Thanks, how did you undo the update after you clicked on another item?Tentage
It's a while ago. I would think you need to reset the formerly selected marker before or just iterate all markers.Lilylilyan
To avoid iterating over markers, I remembered the last selected marker and just undid the changes to the last marker before updateing the next one. ThanksTentage
I just did this, but the icon doesn't change. Do you guys call mClusterManager.cluster()?Rubric
@Tentage how did you remember the last selected marker ?Sortition
J
5

Thanks to @JJD, I wrote the same in Kotlin.

private var selectedBitmap: BitmapDescriptor? = null
private var unselectedBitmap: BitmapDescriptor? = null
private var lastMarker: Marker? = null
private var clusterManager: ClusterManager<StationClusterItem>? = null

override fun onMapReady(googleMap: GoogleMap) {
    this.googleMap = googleMap

    clusterManager = ClusterManager(context!!, googleMap)
    val clusterRenderer = MarkerClusterRenderer(context!!, googleMap, clusterManager!!,
        unselectedBitmap!!)
    clusterManager!!.renderer = clusterRenderer
    // Add your items to the ClusterManager here with clusterManager?.addItem().
    // Better is in background thread.
    val boundsBuilder = LatLngBounds.Builder()
    ...

    clusterManager!!.cluster()
    // Add this listener to make ClusterManager correctly zoom clusters and markers.
    googleMap.setOnCameraIdleListener(clusterManager)

    // This method is needed to use setOnClusterItemClickListener.
    googleMap.setOnMarkerClickListener(clusterManager)
    clusterManager!!.setOnClusterItemClickListener { item ->
        lastMarker?.setIcon(unselectedBitmap)
        lastMarker = clusterRenderer.getMarker(item) // Here we access a selected marker.
        lastMarker?.setIcon(selectedBitmap)
        // false - to show info window. See GoogleMap.InfoWindowAdapter.
        // true - to hide info window.
        false
    }
}

class MarkerClusterRenderer(
    val context: Context,
    val map: GoogleMap,
    clusterManager: ClusterManager<StationClusterItem>,
    private val markerBitmap: BitmapDescriptor
) : DefaultClusterRenderer<StationClusterItem>(context, map, clusterManager) {

    init {
        minClusterSize = 1 // 2, 3 or more, as you wish.
    }    

    override fun onBeforeClusterItemRendered(item: StationClusterItem?,
                                             markerOptions: MarkerOptions?) {
        super.onBeforeClusterItemRendered(item, markerOptions)
        markerOptions?.icon(markerBitmap)
    }
}

UPDATE

I got an exception: java.lang.IllegalArgumentException: Unmanaged descriptor when clicked markers (in a line lastMarker?.setIcon(unselectedBitmap)).

After studying IllegalArgumentException: Unmanaged descriptor using gms.maps.model.Marker.setIcon I rewrote that listener:

private var selectedItem: StationClusterItem? = null

    clusterManager!!.setOnClusterItemClickListener { item ->
        if (selectedItem != null) {
            // Set here a reference to a previous marker.
            // We save a reference to a previous item, not to a marker.
            val lastMarker = clusterRenderer.getMarker(selectedItem)
            lastMarker?.setIcon(unselectedBitmap)
        }
        selectedItem = item
        // Now get a reference to a selected marker.
        val newMarker = clusterRenderer.getMarker(item)
        newMarker?.setIcon(selectedBitmap)
        false
    }
Jakoba answered 18/12, 2018 at 9:26 Comment(2)
Thanks for notice IllegalArgumentExceptionCoenocyte
@Vlad, thanks! Also I understood, that false in setOnClusterItemClickListener affects on info window, see https://mcmap.net/q/593569/-map-markers-with-text-in-google-maps-android-api-v2.Jakoba
A
1

The previous answer doesn't work correctly for me if a marker became unselected programmatically for a random reason (none is selected).

I added this code to the DefaultClusterRenderer inheritor

init {
    viewModel.selectedItem.observeForever {
        if (it == null) {
            clusterManager.cluster()
        }
    }
}

Don't forget to destroy it after this renderer lifecycle ends. And actually, it's better to use outside of the renderer. I use it in my Activity

private fun onMapClickListener(){
    if (viewModel.selectedItem.value != null) clusterManager.cluster()
}

And the default icon changing code (I'm simply switching icon color here):

override fun onBeforeClusterItemRendered(item: ClusterMarker, markerOptions: MarkerOptions) {
    markerOptions.icon(getMarkerColor(item))
}

override fun onClusterItemUpdated(item: ClusterMarker, marker: Marker) {
    marker.setIcon(getMarkerColor(item))
}

private fun getMarkerColor(item: ClusterMarker): BitmapDescriptor {
    val color = if (item.id == viewModel.selectedItem.value?.id) BitmapDescriptorFactory.HUE_ORANGE
    else item.getColor()
    return BitmapDescriptorFactory.defaultMarker(color)
Arium answered 3/7, 2020 at 21:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.