在地圖上最常見的物件就是 Marker (官方翻譯為標記),在地圖上它代表的是一個點的位置,所以它所關聯的空間資訊就是經緯度。
話不多說,就讓我們進到實際製作的階段吧。
還記得前面兩天的 TileOverlay 都是透過TileOverlayOptions 來設定,並透過 addTileOverlay() 來加入到 GoogleMap 上。
Marker 概念上也是如此。
Marker 的樣式。GoogleMap.addMarker() 用這個方法在地圖上建立 Marker。private var googleMap: GoogleMap? = null
private var simpleMarker: Marker? = null
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(binding.root)
    binding.mapView.onCreate(savedInstanceState)
    binding.mapView.getMapAsync {
        // 略...
        
        simpleMarker = addSimpleMarker(it)
    }
}
/**
 * 加入基本款的標記到地圖並取得 Marker 物件
 *
 * @param map
 * @return Marker
 */
private fun addSimpleMarker(map: GoogleMap): Marker? {
    val taichungOpera = LatLng(24.1627, 120.6403)
    return map.addMarker(
        MarkerOptions()
            .position(taichungOpera)
    )
}
跟 TileOverlay 一樣,呼叫 Marker 中的 remove() 方法,即可將 Marker 從地圖上移除。
simpleMarker?.remove()
Marker 的 Icon 除了預設的紅色外,還有其他顏色可以設定。
不過在那之前,要先知道,Marker 的 Icon 樣式是透過 MarkerOptions().icon() 來做設定。icon() 所需要傳入的 BitmapDescriptor 參數,則可以透過 BitmapDescriptorFactory。
像是以下的範例,會是在台中中央公園的點位上,加上一個藍色的 Marker。
private fun addBlueMarkers(map: GoogleMap) {
    val centralPark = LatLng(24.1858, 120.6533)
    map.addMarker(MarkerOptions()
        .position(centralPark)
        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))
    )
}

除了 BitmapDescriptorFactory.HUE_BLUE (藍色) 外,還有其他種顏色可選。

如果都沒有喜歡的,沒關係,明天的客製化 Marker 會聊到如何自定義。
除了調整 Marker 的圖示外,透過 MarkerOptions() 的 title() 跟 snippet(),可以讓 Marker 在點擊時,在 Marker 上方出現一個小資訊視窗。
private fun addMarkerWithInfo(map: GoogleMap) {
    val university = LatLng(24.1789, 120.6464)
    map.addMarker(MarkerOptions()
        .position(university)
        // 黑體標題
        .title("逢甲大學")
        // 灰體副標題
        .snippet("${university.latitude} , ${university.longitude}")
        .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE))
    )
}
除了預設的 UI,也是可以透過 InfoWindowAdapter 自定如下方的 InfoWindow。

圖片來源:官方文件
但以我自己的經驗,實務上比較少使用這個 API,反而圖標上的資訊通常會搭配其他客製化 UI,如 BottomSheet 或 Dialog 來呈現。
有興趣的人可以參考官方文件的說明與範例,如果有遇到問題,也歡迎在下方留言區討論~
GoogleMap.OnMarkerClickListener
點擊事件指的是使用者點擊 Marker 所觸發的事件,實際的實作方式如下。
點擊事件的監聽是設定在 GoogleMap 之上,意味著所有的 Marker 的點擊事件都會透過這個 OnMarkerClickListener.onMarkerClick(marker): Boolean 傳出。
// 設定地圖的 Marker 點擊事件監聽
map.setOnMarkerClickListener { clickedMarker ->
    // 將被點擊的 Marker 的標題與經緯度取出並用 Toast 顯示
    Toast.makeText(
        this@MarkerActivity,
        "${clickedMarker.title} / ${clickedMarker.position.latitude} , ${clickedMarker.position.longitude}",
        Toast.LENGTH_LONG
    ).show()
    
    // 回傳 false 表示點擊事件會繼續傳遞,讓 SDK 執行預設的行為
    return@setOnMarkerClickListener false
}

這個點擊事件要特別注意的事情是,當 onMarkerClicked() 的回傳值設為
true: 代表點擊事件會完全由我們處理,預設的 Marker 點擊行為不會被觸發。false: 反之,預設的 Marker 行為會按照預設的模式執行。預設的點擊行為:地圖將 Marker 移至目前視窗的中心,如有 InfoWindow 則會顯示。
到目前為止所介紹的 Marker 都是固定在特定點位,而可拖曳的 Marker 是可以讓使用者長按並移動它在地圖上的位置。
以下範例,我們建立了一個位在台中青海家樂福的綠色 Marker,並使用 draggable(true) 讓 Marker 可以被拖曳。
private fun addDraggableMarker(map: GoogleMap) {
    val carrefour = LatLng(24.1702, 120.6447)
    map.addMarker(
        MarkerOptions()
            .position(carrefour)
            .draggable(true)
            .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_GREEN))
    )
}
這個拖曳功能比較常見的應用,就是讓使用者在圖面上標記經緯度。
跟點擊事件一樣,拖曳事件的監聽一樣是設定在 GoogleMap 上,透過 setOnMarkerDragListener() 加入 OnMarkerDragListener 介面負責監聽。
map.setOnMarkerDragListener(object : OnMarkerDragListener{
    override fun onMarkerDrag(marker: Marker) {
        // Marker 移動中
        Log.d(TAG, "onMarkerDrag: 移動中")
    }
    override fun onMarkerDragEnd(marker: Marker) {
        // Marker 移動結束
        Toast.makeText(
            this@MarkerActivity,
            "Marker 移動結束",
            Toast.LENGTH_SHORT
        ).show()
    }
    override fun onMarkerDragStart(marker: Marker) {
        // Marker 開始移動
        Toast.makeText(
            this@MarkerActivity,
            "Marker 移動開始",
            Toast.LENGTH_SHORT
        ).show()
    }
})
以上就是基本款的 Marker 設定與常見的互動事件,明天我們會接著看 Marker 的客製化。
那就一樣,明天見啦~![]()