Kahibaro
Discord Login Register

28.2 Markers and Map Events

Understanding Markers on Google Maps

When you start working with maps, markers are usually the first interactive element you add. A marker is a small icon that you place at a specific latitude and longitude on the map. It can show a title and an optional snippet of text when the user taps it.

In Android with the Google Maps SDK, you work with markers through the Marker class and create them using a MarkerOptions object. You normally add markers in the onMapReady callback, where you already have access to the GoogleMap instance.

A typical sequence is to create a LatLng that holds the coordinates, configure a MarkerOptions, and then add the marker to the map. The addMarker function of GoogleMap returns a Marker reference, which you can store if you need to update or remove that marker later.

Here is a simple example that places a marker at a given location and moves the camera to it:

override fun onMapReady(googleMap: GoogleMap) {
    val sydney = LatLng(-34.0, 151.0)
    val marker = googleMap.addMarker(
        MarkerOptions()
            .position(sydney)
            .title("Marker in Sydney")
            .snippet("Population: about 5 million")
    )
    googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney, 10f))
}

The title appears in bold at the top of the info window when the user taps the marker. The snippet appears as smaller text below the title.

Always keep a reference to markers you may need to change or remove later. You can store them in properties or collections instead of calling addMarker again for the same location.

Customizing Marker Appearance

Markers can be customized to better represent different types of locations, such as restaurants, user locations, or events. The simplest marker uses the default Google Maps pin, but you can change color, icon image, and visibility.

To customize appearance, you modify MarkerOptions before adding it to the map.

For example, to change the marker hue using the built-in default marker variations:

val restaurantLocation = LatLng(37.422, -122.084)
val restaurantMarker = googleMap.addMarker(
    MarkerOptions()
        .position(restaurantLocation)
        .title("Restaurant")
        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE))
)

You can also use your own drawable as the icon. In this case you usually create a BitmapDescriptor from a resource:

val customIcon = BitmapDescriptorFactory.fromResource(R.drawable.ic_custom_marker)
val customMarker = googleMap.addMarker(
    MarkerOptions()
        .position(LatLng(37.422, -122.084))
        .title("Custom Icon")
        .icon(customIcon)
)

If you need more control, such as scaling or drawing complex shapes, you typically convert a Drawable or a View to a Bitmap and then pass it to BitmapDescriptorFactory.fromBitmap. You should be careful with performance when creating many custom marker bitmaps, because bitmaps use memory on the device.

You can also control other visual aspects such as visibility and opacity:

marker.isVisible = true
marker.alpha = 0.7f        // 0.0f invisible, 1.0f fully opaque
marker.rotation = 45f      // in degrees

This is useful when you want to fade markers, hide them temporarily, or rotate them to indicate direction, for example in a navigation scenario.

Managing Multiple Markers

In many real applications, you handle more than one marker. For example, you might show a list of stores or points of interest on the map. In that case you usually loop over your data items, create a LatLng for each, and add markers.

You can keep track of those markers by storing them in a MutableList<Marker> or a MutableMap keyed by some identifier.

Here is a basic example that adds markers for a list of locations:

data class Place(
    val id: String,
    val name: String,
    val position: LatLng
)
private val placeMarkers = mutableMapOf<String, Marker>()
fun addPlacesToMap(googleMap: GoogleMap, places: List<Place>) {
    for (place in places) {
        val marker = googleMap.addMarker(
            MarkerOptions()
                .position(place.position)
                .title(place.name)
        )
        if (marker != null) {
            placeMarkers[place.id] = marker
        }
    }
}

Later, if a place changes or should be removed, you can use this map to update or delete the marker:

fun removePlaceMarker(placeId: String) {
    val marker = placeMarkers.remove(placeId)
    marker?.remove()
}

Do not call addMarker repeatedly for the same logical item without removing the old marker. This can leave old markers on the map and confuse the user. Use stored references to update or remove them instead.

Working with Info Windows

Each marker can show an info window when the user taps the marker. The info window is a popup above the marker that displays the marker title and snippet by default.

If you want to show the info window automatically after adding a marker, you can call showInfoWindow on the Marker:

val marker = googleMap.addMarker(
    MarkerOptions()
        .position(LatLng(37.422, -122.084))
        .title("Auto opened")
        .snippet("This info window is visible immediately")
)
marker?.showInfoWindow()

You can also hide an info window programmatically using hideInfoWindow.

If you need more than a simple title and snippet, you can provide a custom info window. For that you implement GoogleMap.InfoWindowAdapter and register it on the map using googleMap.setInfoWindowAdapter. The adapter lets you return your own View for the full window or only the contents area, and you can inflate a layout and populate it with data.

Basic Map Event Listeners

Map events are callbacks that notify you when the user interacts with the map or when the state of the map changes. You typically register listeners on the GoogleMap instance.

Some of the most common map-level events include taps and long presses on the map surface, as well as camera movements such as scrolling or zooming. In this chapter we focus on those that are directly related to markers and user interaction with the map.

To enable a listener, you usually call a method such as setOnMapClickListener and pass an object or lambda that implements the interface.

For example, to respond when the user taps on any position on the map, not on a marker, you can do the following:

googleMap.setOnMapClickListener { latLng ->
    // latLng is the position where the user tapped
    // For example, add a marker at that point
    googleMap.addMarker(
        MarkerOptions()
            .position(latLng)
            .title("New marker")
    )
}

To react to long presses, use setOnMapLongClickListener. This is often useful when you want the user to select a location by pressing and holding:

googleMap.setOnMapLongClickListener { latLng ->
    // For example, clear old markers and set a single selection marker
    googleMap.clear()
    googleMap.addMarker(
        MarkerOptions()
            .position(latLng)
            .title("Selected location")
    )
}

These listeners help you turn the map into an interactive surface where the user can place or modify markers using gestures.

Marker Click and Info Window Events

The most common event related to markers is the marker click. You can react to marker clicks by registering a GoogleMap.OnMarkerClickListener.

The listener provides the Marker that was clicked. You can then inspect its properties or use it as a key to your own data.

A simple example that shows a toast when a marker is clicked can look like this:

googleMap.setOnMarkerClickListener { marker ->
    Toast.makeText(
        this,
        "Clicked: ${marker.title}",
        Toast.LENGTH_SHORT
    ).show()
    false
}

The boolean value returned from the listener controls what happens next. If you return false, the default behavior continues, which usually means the map moves the camera slightly and opens the marker info window. If you return true, you tell the map that you have handled the event and it should not perform the default action.

Return true from OnMarkerClickListener only if you want to completely override default behavior and prevent the info window from opening automatically.

You can also listen for info window clicks with setOnInfoWindowClickListener. This is useful when you treat a marker as a preview and want to open a detail screen when the user taps its info window:

googleMap.setOnInfoWindowClickListener { marker ->
    // For example, open details for this location
    openDetailsScreenFor(marker)
}

To detect when the user begins or ends interacting with an info window, there are additional listeners such as setOnInfoWindowCloseListener and setOnInfoWindowLongClickListener.

By combining marker click and info window click listeners, you can build flows where the map acts as a visual index of items that lead to deeper content.

Dragging Markers

Markers can be static or draggable. A draggable marker lets the user press and hold the marker, then move it to a new position. This is common when the user wants to fine tune an address or choose a point on the map.

To make a marker draggable, you set it in MarkerOptions:

val marker = googleMap.addMarker(
    MarkerOptions()
        .position(LatLng(37.422, -122.084))
        .title("Drag me")
        .draggable(true)
)

Then you implement GoogleMap.OnMarkerDragListener to respond to drag events. This listener has three callbacks.

You can register the listener like this:

googleMap.setOnMarkerDragListener(object : GoogleMap.OnMarkerDragListener {
    override fun onMarkerDragStart(marker: Marker) {
        // User started dragging the marker
    }
    override fun onMarkerDrag(marker: Marker) {
        // Called repeatedly while the marker is dragged
        // You could update some UI with marker.position here
    }
    override fun onMarkerDragEnd(marker: Marker) {
        // User released the marker
        val newPosition = marker.position
        // Save or use the new position
    }
})

For tasks such as selecting a delivery address or setting a geofence center, you usually react in onMarkerDragEnd, because the user has finished moving the marker and the final position is stable.

Updating and Removing Markers with Events

As users interact with the map and markers, you often need to update or remove markers dynamically. For example, you might let users tap the map to create a marker, drag it to adjust the position, and tap again to confirm.

Updating a marker uses methods on the Marker instance. For example, you can change its position, title, or snippet:

fun updateMarker(marker: Marker, newPosition: LatLng, newTitle: String) {
    marker.position = newPosition
    marker.title = newTitle
}

Removing a marker is done with marker.remove(). After you remove it, the marker object should not be used again. If you store markers in a list or map, you should remove them there as well to avoid holding references to markers that no longer exist on the map.

Sometimes you want to clear everything from the map in response to an event, such as a button click or a long press. The GoogleMap.clear() function removes all markers, polylines, and overlays that were added programmatically.

Always keep your data model and the map in sync. When you remove a marker from the map, also update any collections or state that refer to it. If you forget, your code may still try to access markers that no longer exist.

Combining Map Events for Interactive Experiences

A powerful part of working with markers and map events is combining different listeners to build natural interactions. For example, you can use OnMapClickListener to create a marker, OnMarkerDragListener to adjust its position, and OnInfoWindowClickListener to confirm or edit details.

Consider a typical flow for selecting a location:

  1. The user taps on the map, and you create a single marker at that position. If a marker already exists, you move it instead of creating another one.
  2. The user drags the marker to fine tune the location. You observe onMarkerDragEnd and update a text field that displays the selected coordinates or address.
  3. The user taps the marker info window to confirm, which triggers navigation to another screen or finishes the selection process.

This pattern uses three listeners and a single marker instance. The map is not filled with duplicates, and your code treats the marker as a visual representation of your underlying location data.

You can adapt similar combinations for many use cases, such as highlighting search results, responding to list item selection by focusing a marker, or letting users add and remove points of interest dynamically.

By carefully using markers and map events, you turn a static map into a responsive interface that feels natural on a touch screen.

Views: 1

Comments

Please login to add a comment.

Don't have an account? Register now!