FindYourCoffee 專案的需求 :
今天要來實作點擊地圖上的標記要跳出顯示咖啡廳的資訊視窗。目前我們使用的是地圖預設的資訊視窗,並使用 title 來顯示咖啡廳名稱,但是,為了提供給使用者更詳細的資訊,所以就來客製化資訊視窗吧!
今天的實作課程 :
這是目前點擊標記後顯示的預設視窗 :
一起來改造它吧~~
目前我們是使用以下的程式碼顯示預設的資訊視窗:
map.addMarker(
MarkerOptions()
.position(position)
.title(title)
.snippet(snippet)
.icon(icon)
)
如果只是要顯示簡單的資料,在 addMarker()
的時候設定 title
和 snipper
即可。
來試著顯示從 API 撈到的全部資訊,要客製化視窗就要先定義好 layout,這邊直接使用先前寫好的 item_cafe.xml,因為筆者現在對 UI 還沒有想法,哈哈 :
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="5dp"
android:elevation="3dp"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/tv_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="00014645-38c8-4eb4-ad9b-faa871d7e511" />
<TextView
android:id="@+id/tv_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="R5小餐館" />
<TextView
android:id="@+id/tv_city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="chiayi" />
<TextView
android:id="@+id/tv_wifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="5" />
<TextView
android:id="@+id/tv_seat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="5" />
<TextView
android:id="@+id/tv_quiet"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="5" />
<TextView
android:id="@+id/tv_tasty"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="5" />
<TextView
android:id="@+id/tv_cheap"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="5" />
<TextView
android:id="@+id/tv_music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="5" />
<TextView
android:id="@+id/tv_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="https://www.facebook.com/r5.bistro" />
<TextView
android:id="@+id/tv_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="嘉義市東區忠孝路205號" />
<TextView
android:id="@+id/tv_latitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="23.48386540" />
<TextView
android:id="@+id/tv_longitude"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="120.45358340" />
<TextView
android:id="@+id/tv_limited_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="maybe" />
<TextView
android:id="@+id/tv_socket"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="maybe" />
<TextView
android:id="@+id/tv_standing_desk"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="no" />
<TextView
android:id="@+id/tv_mrt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="" />
<TextView
android:id="@+id/tv_open_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:textSize="16sp"
tools:text="11:30~21:00" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
在建構子傳入 LayoutInflater
和 PlaceDetails
:
class MainInfoAdapter(private val inflater: LayoutInflater, private val info: PlaceDetailsafe): GoogleMap.InfoWindowAdapter {
override fun getInfoContents(p0: Marker): View? {
TODO("Not yet implemented")
}
override fun getInfoWindow(p0: Marker): View? {
TODO("Not yet implemented")
}
}
Google Map API 會先呼叫 [getInfoWindow(Marker)](https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter?hl=zh-tw#getInfoWindow(com.google.android.gms.maps.model.Marker))
,如果傳回 null
,會接著呼叫 [getInfoContents(Marker)](https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.InfoWindowAdapter?hl=zh-tw#getInfoContents(com.google.android.gms.maps.model.Marker))
。如果還是傳回 null
,就會使用預設資訊視窗。
下圖為官方文件提供的樣式對照圖 :
接著來完成實作 :
class MainInfoAdapter(private val inflater: LayoutInflater, val info: PlaceDetails): GoogleMap.InfoWindowAdapter {
override fun getInfoContents(p0: Marker): View? {
return null
}
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)
info.let {
tvID.text = it.id
tvName.text = it.name
tvCity.text = it.city
}
return view
}
}
接著來到 MainActivity,因為要在點擊標記時顯示資訊視窗,所以先實作 GoogleMap.OnMarkerClickListener
:
class MainActivity : AppCompatActivity(),
GoogleMap.OnMyLocationButtonClickListener,
GoogleMap.OnMyLocationClickListener, OnMapReadyCallback,
GoogleMap.OnMarkerClickListener,
ActivityCompat.OnRequestPermissionsResultCallback {}
在 callback 處顯示當前點擊標記資訊視窗
override fun onMarkerClick(marker: Marker): Boolean {
// 取得當前點擊的咖啡廳資訊
val info = placeDetailsMap[marker.snippet]
map.setInfoWindowAdapter(MainInfoAdapter(layoutInflater, info!!))
Toast.makeText(this, "Click {${marker.title}}", Toast.LENGTH_LONG).show()
return true
}
在 onMapReady()
取得 google map 實例的地方設定 :
override fun onMapReady(googleMap: GoogleMap) {
// return early if the map was not initialised properly
map = googleMap
// 取得位置權限
requestLocationPermissions()
with(map) {
// ...
// 設定標記點擊
setOnMarkerClickListener(this@MainActivity)
}
}
執行看看 :
居然沒有出現資訊視窗,但是確實有出現當前點擊標記的吐司,WHY~
剩下的明天再來繼續討論好了