iT邦幫忙

2023 iThome 鐵人賽

DAY 26
0
Kotlin

喝咖啡要30天?一起用 Kotlin 打造尋找好喝咖啡的 App系列 第 26

Day26 在 Google 地圖上顯示全台咖啡廳資訊 - 5 顯示客製化標記資訊

  • 分享至 

  • xImage
  •  

FindYourCoffee 專案的需求 :

  • [x] 點擊地圖標記顯示商家資訊
  • [x] 顯示我的位置
  • [x] 在地圖上顯示咖啡廳標記
  • [ ] 點擊地圖標記顯示咖啡廳資訊
  • [ ] 顯示我的位置附近的咖啡廳資訊
  • [ ] 加入篩選功能

今天要來實作點擊地圖上的標記要跳出顯示咖啡廳的資訊視窗。目前我們使用的是地圖預設的資訊視窗,並使用 title 來顯示咖啡廳名稱,但是,為了提供給使用者更詳細的資訊,所以就來客製化資訊視窗吧!

今天的實作課程 :

這是目前點擊標記後顯示的預設視窗 :

d26_1.png

一起來改造它吧~~

使用預設的資訊視窗

目前我們是使用以下的程式碼顯示預設的資訊視窗:

map.addMarker(
    MarkerOptions()
    .position(position)
    .title(title)
    .snippet(snippet)
    .icon(icon)
)

如果只是要顯示簡單的資料,在 addMarker() 的時候設定 titlesnipper 即可。

顯示客製化的資訊視窗

定義 layout

來試著顯示從 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>

自定義 GoogleMap.InfoWindowAdapter

在建構子傳入 LayoutInflaterPlaceDetails :

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,就會使用預設資訊視窗。

  • getInfoContents() : 顯示整個資訊視窗的檢視畫面
  • getInfoWindow() : 自訂視窗內容,但仍保留預設的資訊視窗頁框和背景。

下圖為官方文件提供的樣式對照圖 :

d26_2.png

接著來完成實作 :

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)
    }
}

執行看看 :

d26_3.png

居然沒有出現資訊視窗,但是確實有出現當前點擊標記的吐司,WHY~

剩下的明天再來繼續討論好了

今日推推

Yes


上一篇
Day25 在 Google 地圖上顯示全台咖啡廳資訊 - 4 顯示咖啡廳標記
下一篇
Day27 在 Google 地圖上顯示全台咖啡廳資訊 - 5 二戰客製化資訊視窗
系列文
喝咖啡要30天?一起用 Kotlin 打造尋找好喝咖啡的 App30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言