昨天挑戰點擊標記後,跳出自定義的資訊視窗失敗了…先來複習失敗品 :
只出現吐司,不是預期的長相。
目前的邏輯 :
InfoWindowAdapter
設定給 Google Map為了確定是否收到標記的點擊,所以在點擊時會跳出吐司,所以確定了監聽標記沒有問題;那問題可能是出在初始化 InfoWindowAdapter
:
// 取得當前點擊的咖啡廳資訊
val info = placeDetailsMap[marker.snippet]
map.setInfoWindowAdapter(MainInfoAdapter(layoutInflater, info!!))
官方範例和爬文後發現,都是將初始化做在取得 GoogleMap 物件的 onMapReady()
;但當初筆者認為要在點擊時才傳入當前資料,所以沒有照做,以下是官方範例的程式碼 :
override fun onMapReady(googleMap: GoogleMap?) {
// ...
with(map) {
// Setting an info window adapter allows us to change the both the contents and
// look of the info window.
setInfoWindowAdapter(CustomInfoWindowAdapter())
}
// Add lots of markers to the googleMap.
addMarkersToMap()
}
假設問題是出在這邊,那麼又該如何解決傳入自定義物件 PlaceDetails
?
在將標記加入至地圖時,我們也會一併設定 title()
和 snippet()
,title 目前是設定咖啡廳的名稱,snippet 倒是隨便給的 id 而已,那就拿 snippet 開刀看看?
這是目前設定標記的程式碼 :
private fun addMarkersToMap() {
// place markers for each of the defined locations
placeDetailsMap.keys.map {
with(placeDetailsMap.getValue(it)) {
map.addMarker(
MarkerOptions()
.position(position)
.title(title)
.snippet(it)
.icon(icon)
)
}
}
}
因為 snippet
吃的是 string
,筆者想直接使用 Moshi
套件 將自定義的 PlaceDetails
資料轉成 JsonString
餵它,然後在自定義的 InfoAdapter 內在轉換成 PlaceDetails
,好像有點偷吃步,路過的大神也歡迎分享下作法 :>
// Moshi
implementation 'com.squareup.moshi:moshi:1.13.0'
implementation "com.squareup.moshi:moshi-kotlin:1.13.0"
先到 MainInfoAdapter.kt,修改下建構子 :
class MainInfoAdapter(private val inflater: LayoutInflater):
GoogleMap.InfoWindowAdapter {}
在 getInfoWindow()
將資料皆出來,並顯示於 UI :
override fun getInfoWindow(marker: Marker): View? {
val view = inflater.inflate(R.layout.item_cafe, null)
val tvID = view.findViewById<TextView>(R.id.tv_id)
val tvName = view.findViewById<TextView>(R.id.tv_name)
val tvCity = view.findViewById<TextView>(R.id.tv_city)
// 從 snippet 取出詳細資訊
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val adapter = moshi.adapter(PlaceDetails::class.java)
val info = marker.snippet?.let { adapter.fromJson(it) }
info?.let {
tvID.text = it.id
tvName.text = it.name
tvCity.text = it.city
// ...
}
return view
}
這邊要改的東西有點多,總之先把點擊標記的程式碼拿掉。
接著改在 onMapReady()
設定 adapter 給地圖 :
map.setInfoWindowAdapter(adapter)
override fun onMapReady(googleMap: GoogleMap) {
// return early if the map was not initialised properly
map = googleMap
// 取得位置權限
requestLocationPermissions()
with(map) {
// Set the map type
mapType = com.google.android.gms.maps.GoogleMap.MAP_TYPE_NORMAL
// UI 設定 : 交通 、指南針、縮放按鈕
isTrafficEnabled = true
uiSettings.isCompassEnabled = true
uiSettings.isZoomControlsEnabled = true
// 設置預設標記 - 台北
val taipei = LatLng(25.0329694, 121.5654177) // 例如,台北的经纬度
moveCamera(com.google.android.gms.maps.CameraUpdateFactory.newLatLngZoom(taipei, 10f))
addMarker(
MarkerOptions().position(taipei).title("台北")
)
// 我的位置點擊
setOnMyLocationButtonClickListener(this@MainActivity)
setOnMyLocationClickListener(this@MainActivity)
// 設定自定義資訊視窗
val adapter = MainInfoAdapter(layoutInflater)
map.setInfoWindowAdapter(adapter)
}
}
接著在取得 API 資料處重新設定咖啡廳的詳細資訊 :
cafes.forEach {
// 取出咖啡廳的經緯度
placesMap = mutableMapOf(
it.id to LatLng(it.latitude.toDouble(), it.longitude.toDouble())
)
// 設定咖啡廳的詳細資訊
placeDetailsMap[it.id] = PlaceDetails(
title = it.name,
id = it.id,
name = it.name,
city = it.city,
wifi = it.wifi,
seat = it.seat,
quiet = it.quiet,
tasty = it.tasty,
cheap = it.cheap,
music = it.music,
url = it.url,
address = it.address,
latitude = it.latitude,
longitude = it.longitude,
limited_time = it.limited_time,
socket = it.socket,
mrt = it.mrt,
standing_desk = it.standing_desk,
open_time = it.open_time
)
}
最後是調整標記資訊,改用 Moshi
將資料轉換成 JsonString
,設定給 snippet
:
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val adapter = moshi.adapter(PlaceDetails::class.java)
.snippet(adapter.toJson(this))
private fun addMarkersToMap() {
val moshi = Moshi.Builder().add(KotlinJsonAdapterFactory()).build()
val adapter = moshi.adapter(PlaceDetails::class.java)
// place markers for each of the defined locations
placeDetailsMap.keys.map {
with(placeDetailsMap.getValue(it)) {
map.addMarker(
MarkerOptions()
.position(LatLng(latitude.toDouble(), longitude.toDouble()))
.title(title)
.snippet(adapter.toJson(this))
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_AZURE))
)
}
}
}
好了~~~試試看 :
成功了~~~ 哭出乃 !
不知不覺我們的專案大體成形了,至少也有個87%,豪開心~~~