Day 29:Google Map 自订资讯视窗

本篇文章同步发表在 HKT 线上教室 部落格,线上影音教学课程已上架至 UdemyYoutube 频道。另外,想追踪更多相关技术资讯,欢迎到 脸书粉丝专页 按赞追踪喔~

程序码范例

范例名称:Google Map 自订资讯视窗
开发人员:HKT (侯光灿)
程序语言:Kotlin
开发环境:Android Studio 4.1.2 & Android 11 & Kotlin 1.4.30
授权范围:使用时必须注明出处且不得为商业目的之使用
范例下载点:点我下载

昨天,我们使用了 Google Map 官方预设的资讯视窗,真的很简洁、很方便,立刻可上手使用,但相对显示上比较阳春。若我们想要显示更多资讯内容或样式,可以透过自定义资讯视窗来解决这个问题。

自定义资讯视窗 Wireframe

自定义资讯视窗布局

在 layout 资料夹新增 info_window.xml,跟RecyleView 布局一样,但因为资讯视窗关系,因为没有 parent 可以对齐宽度和高度,所以不能采用 match_parent ,需要设定一个大小或根据内容决定整体宽度和高度大小。所以布局相似,但我们需修改一下相关设定。完整细节如下:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginLeft="10dp"
    android:layout_marginTop="10dp"
    android:layout_marginRight="10dp"
    app:cardCornerRadius="8dp">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/layout_item"
        android:layout_width="300dp"
        android:layout_height="150dp"
        android:background="?android:attr/selectableItemBackground"
        android:clickable="true"
        android:focusable="true"
        android:paddingBottom="20dp">


        <TextView
            android:id="@+id/tv_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="15dp"
            android:text="药局名称"
            android:textColor="#424242"
            android:textSize="30dp"
            app:layout_constraintBottom_toTopOf="@+id/layout_adult"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/layout_adult"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/bg_amount_info"
            android:padding="10dp"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/layout_child"
            app:layout_constraintTop_toBottomOf="@+id/tv_name">

            <TextView
                android:id="@+id/tv_adult"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="成人口罩"
                android:textColor="#ffffff"
                android:textSize="20dp"
                android:textStyle="bold"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/tv_adult_amount"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="口罩数量"
                android:textColor="#ffffff"
                android:textSize="16dp"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toBottomOf="@id/tv_adult" />
        </androidx.constraintlayout.widget.ConstraintLayout>

        <androidx.constraintlayout.widget.ConstraintLayout
            android:id="@+id/layout_child"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:background="@drawable/bg_amount_info"
            android:padding="10dp"
            app:layout_constraintLeft_toRightOf="@+id/layout_adult"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/tv_name">

            <TextView
                android:id="@+id/tv_child"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="小孩口罩"
                android:textColor="#ffffff"
                android:textSize="20dp"
                android:textStyle="bold"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

            <TextView
                android:id="@+id/tv_child_amount"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="口罩数量"
                android:textColor="#ffffff"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toBottomOf="@id/tv_child" />
        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>

自定义 InfoWindowAdapter

新增 MyInfoWindowAdapter.kt 覆写实作 GoogleMap.InfoWindowAdapter 的方法:

package com.thishkt.pharmacydemo.adapter

import android.app.Activity
import android.content.Context
import android.view.View
import android.widget.TextView
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.model.Marker
import com.thishkt.pharmacydemo.R

class MyInfoWindowAdapter(context: Context) : GoogleMap.InfoWindowAdapter {

    //指定自定义资讯视窗,显示布局的样式
    var mWindow: View = (context as Activity).layoutInflater.inflate(R.layout.info_window, null)

    private fun render(marker: Marker, view: View) {

        val tvName = view.findViewById<TextView>(R.id.tv_name)
        val tvAdultAmount = view.findViewById<TextView>(R.id.tv_adult_amount)
        val tvChildAmount = view.findViewById<TextView>(R.id.tv_child_amount)

        //透过 marker.snippet 传递口罩数量,将资料拆解後,指定到对应的 UI 栏位上显示
        val mask = marker.snippet.toString().split(",")

        //药局名称
        tvName.text = marker.title
        
        //成人口罩数量
        tvAdultAmount.text = mask[0]
        
        //小孩口罩数量
        tvChildAmount.text = mask[1]
    }

    override fun getInfoContents(marker: Marker): View {
        render(marker, mWindow)
        return mWindow
    }

    override fun getInfoWindow(marker: Marker): View? {
        return null
    }
}

设定 InfoWindowAdapter

最後设定我们的地图 googleMap ,载入我们自定义的资讯视窗,MyInfoWindowAdapter

currLocationMarker?.remove()
googleMap?.setInfoWindowAdapter(MyInfoWindowAdapter(mContext))
currLocationMarker = googleMap?.addMarker(
    MarkerOptions()
        .position(currentLocation)
        .title("现在位置")
        .snippet("100,66")
)
currLocationMarker?.showInfoWindow()

输出结果

资讯视窗点击事件

加入 setOnInfoWindowClickListener 处理事件,这边简单示范,点击资讯视窗会在 Log 视窗印出该间药局名称:

googleMap?.setOnInfoWindowClickListener {
    Log.d(
        "HKT",
        "title: ${currLocationMarker?.title}"
    )
}

参考资料

HKT 线上教室
https://tw-hkt.blogspot.com/

Freepik
https://www.freepik.com/


那今天【iThome 铁人赛】就介绍到这边罗~

顺带一提,KT 线上教室,脸书粉丝团,会不定期发布相关资讯,不想错过最新资讯,不要忘记来按赞,追踪喔!也欢迎大家将这篇文章分享给更多人喔。

我们明天再见罗!!!掰掰~


<<:  Day 29 - AWS Lambda 接收参数查询 Dynamodb

>>:  自动化测试,让你上班拥有一杯咖啡的时间 | Day 15 - 设定环境变量

RDS 即时监看

除了警示之外, DBA也可能需要即时监看, 立即找出问题所在. RDS也提供即时监看面板. 在资料库...

浏览器物件模型 BOM

音乐请下BOOM BOOM POW BOM 系虾米? Browser Object Model 的缩...

Day 5 - 安全签章: 取得 SHA256 加密後的 Sign 值

图 5-1: 各栏位资料范例 在 Day 2 我们知道了取得 Nonce 的方法。在 Day 3 ...

Ruby on Rails RESTful 网址设计

REST 是 Representational State Transfer 的缩写,中⽂翻译成「具...

【Day10】模组化及引用模组

模组 在一个 .V 档案里面,可以有很多个 module,但是 Top Module 只会有一个,所...