เพิ่มแผนที่ในแอป Android (Kotlin)

1. ก่อนที่คุณจะเริ่มต้น

Codelab นี้จะสอนวิธีผสานรวม Maps SDK สําหรับ Android กับแอปของคุณ และใช้ฟีเจอร์หลักโดยการสร้างแอปที่แสดงแผนที่ของร้านจักรยานในซานฟรานซิสโก แคลิฟอร์เนีย สหรัฐอเมริกา

f05e1ca27ff42bf6.png

สิ่งที่ต้องมีก่อน

  • ความรู้เบื้องต้นเกี่ยวกับการพัฒนา Kotlin และ Android

สิ่งที่คุณจะทํา

  • เปิดใช้และใช้ Maps SDK สําหรับ Android เพื่อเพิ่ม Google Maps ลงในแอป Android
  • เพิ่ม ปรับแต่ง และจัดกลุ่มเครื่องหมาย
  • วาดเส้นและรูปหลายเหลี่ยมบนแผนที่
  • ควบคุมมุมมองของกล้อง

สิ่งที่ต้องมี

2. ตั้งค่า

สําหรับขั้นตอนการเปิดใช้ต่อไปนี้ คุณต้องเปิดใช้ Maps SDK สําหรับ Android

ตั้งค่า Google Maps Platform

หากยังไม่มีบัญชี Google Cloud Platform และโปรเจ็กต์ที่เปิดใช้การเรียกเก็บเงิน โปรดดูคู่มือการเริ่มต้นใช้งาน Google Maps Platform เพื่อสร้างบัญชีสําหรับการเรียกเก็บเงินและโปรเจ็กต์

  1. ใน Cloud Console ให้คลิกเมนูแบบเลื่อนลงของโปรเจ็กต์ แล้วเลือกโปรเจ็กต์ที่ต้องการใช้สําหรับ Codelab นี้

  1. เปิดใช้ Google Maps Platform API และ SDK ที่จําเป็นสําหรับ Codelab นี้ใน Google Cloud Marketplace โดยทําตามขั้นตอนในวิดีโอนี้หรือเอกสารนี้
  2. สร้างคีย์ API ในหน้าข้อมูลเข้าสู่ระบบของ Cloud Console คุณสามารถทําตามขั้นตอนในวิดีโอนี้หรือเอกสารนี้ คําขอทั้งหมดสําหรับ Google Maps Platform ต้องใช้คีย์ API

3. การเริ่มใช้งานอย่างง่าย

โค้ดเริ่มต้นเพื่อช่วยให้คุณเริ่มต้นทําสิ่งต่อไปนี้ได้พร้อมด้วย Codelab เพื่อช่วยให้คุณเริ่มต้นใช้งานได้เร็วที่สุด คุณยินดีข้ามไปที่โซลูชันนี้ แต่ถ้าต้องการทําตามขั้นตอนทั้งหมดในการสร้างด้วยตนเอง ให้อ่านต่อไป

  1. โคลนที่เก็บหากคุณติดตั้ง git
git clone https://github.com/googlecodelabs/maps-platform-101-android.git

หรือจะคลิกปุ่มต่อไปนี้เพื่อดาวน์โหลดซอร์สโค้ดก็ได้

  1. เมื่อได้รับรหัสแล้ว ให้เปิดโปรเจ็กต์ที่พบในไดเรกทอรี starter ใน Android Studio

4. เพิ่ม Google Maps

ในส่วนนี้ คุณจะต้องเพิ่ม Google Maps เพื่อให้โหลดเมื่อคุณเปิดแอป

d1d068b5d4ae38b9.png

เพิ่มคีย์ API

คุณต้องระบุคีย์ API ที่คุณสร้างในขั้นตอนก่อนหน้าให้กับแอปเพื่อให้ Maps SDK สําหรับ Android เชื่อมโยงคีย์กับแอปได้

  1. หากต้องการดําเนินการนี้ ให้เปิดไฟล์ชื่อ local.properties ในไดเรกทอรีรากของโปรเจ็กต์ (ระดับเดียวกับ gradle.properties และ settings.gradle)
  2. ในไฟล์ดังกล่าว ให้กําหนดคีย์ใหม่ GOOGLE_MAPS_API_KEY โดยมีค่าเป็นคีย์ API ที่คุณสร้าง

local.properties

GOOGLE_MAPS_API_KEY=YOUR_KEY_HERE

โปรดทราบว่า local.properties จะแสดงอยู่ในไฟล์ .gitignore ในที่เก็บ Git เนื่องจากคีย์ API ของคุณถือเป็นข้อมูลที่ละเอียดอ่อนและไม่ควรเช็คอินการควบคุมแหล่งที่มาหากเป็นไปได้

  1. จากนั้น หากต้องการแสดง API เพื่อให้ใช้ทั่วทั้งแอป ให้ใส่ปลั๊กอิน Secrets Gradle Plugin for Android ในไฟล์ build.gradle ของแอปที่อยู่ในไดเรกทอรี app/ และเพิ่มบรรทัดต่อไปนี้ภายในบล็อก plugins

build.gradle ระดับแอป

plugins {
    // ...
    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}

นอกจากนี้ คุณจะต้องแก้ไขไฟล์ build.gradle ระดับโปรเจ็กต์ให้รวม pathpath ต่อไปนี้

build.gradle ระดับโปรเจ็กต์

buildscript {
    dependencies {
        // ...
        classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:1.3.0"
    }
}

ปลั๊กอินนี้จะทําให้คีย์ที่กําหนดไว้ภายในไฟล์ local.properties ใช้เป็นตัวแปรบิลด์ในไฟล์ Manifest ของ Android และเป็นตัวแปรในคลาส BuildConfig ที่ Gradle สร้างขึ้นในเวลาบิลด์ การใช้ปลั๊กอินนี้จะนําโค้ด Boilerplate ที่ต้องใช้ในการอ่านพร็อพเพอร์ตี้ออกจาก local.properties เพื่อให้เข้าถึงได้ทั่วทั้งแอป

เพิ่มทรัพยากร Dependency ของ Google Maps

  1. ตอนนี้คุณสามารถเข้าถึงคีย์ API ภายในแอปได้แล้ว ขั้นตอนถัดไปคือเพิ่มทรัพยากร Dependency ของ Maps SDK สําหรับ Android ลงในไฟล์ build.gradle ของแอป

ในโปรเจ็กต์เริ่มต้นที่มาพร้อมกับ Codelab นี้ เราได้เพิ่มทรัพยากร Dependency นี้ไว้แล้ว

build.gradle

dependencies {
   // Dependency to include Maps SDK for Android
   implementation 'com.google.android.gms:play-services-maps:17.0.0'
}
  1. ต่อไปให้เพิ่มแท็ก meta-data ใหม่ใน AndroidManifest.xml เพื่อส่งผ่านคีย์ API ที่สร้างไว้ในขั้นตอนก่อนหน้า ในการดําเนินการนี้ ให้เปิดไฟล์นี้ใน Android Studio แล้วเพิ่มแท็ก meta-data ต่อไปนี้ภายในออบเจ็กต์ application ในไฟล์ AndroidManifest.xml ซึ่งจะอยู่ใน app/src/main

AndroidManifest.xml

<meta-data
   android:name="com.google.android.geo.API_KEY"
   android:value="${GOOGLE_MAPS_API_KEY}" />
  1. ต่อไป สร้างไฟล์เลย์เอาต์ใหม่ชื่อ activity_main.xml ในไดเรกทอรี app/src/main/res/layout/ แล้วกําหนดสิ่งต่อไปนี้

activity_main.xml

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity">

   <fragment
       class="com.google.android.gms.maps.SupportMapFragment"
       android:id="@+id/map_fragment"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />

</FrameLayout>

เลย์เอาต์นี้มี FrameLayout รายการที่มี SupportMapFragment อยู่เพียงรายการเดียว ส่วนย่อยนี้มีออบเจ็กต์ GoogleMaps ที่สําคัญซึ่งคุณใช้ในขั้นตอนต่อๆ ไป

  1. สุดท้าย ให้อัปเดตคลาส MainActivity ใน app/src/main/java/com/google/codelabs/buildyourfirstmap โดยการเพิ่มโค้ดต่อไปนี้เพื่อลบล้างเมธอด onCreate เพื่อให้คุณตั้งค่าเนื้อหาด้วยเลย์เอาต์ใหม่ที่คุณเพิ่งสร้างได้

กิจกรรมหลัก

override fun onCreate(savedInstanceState: Bundle?) {
   super.onCreate(savedInstanceState)
   setContentView(R.layout.activity_main)
}
  1. ทีนี้ดําเนินการต่อและเรียกใช้แอป คุณจะเห็นการโหลดแผนที่บนหน้าจอของอุปกรณ์

5. การจัดรูปแบบแผนที่ในระบบคลาวด์ (ไม่บังคับ)

คุณสามารถปรับแต่งรูปแบบแผนที่โดยใช้การจัดรูปแบบแผนที่ในระบบคลาวด์

สร้างรหัสแผนที่

หากคุณยังไม่ได้สร้างรหัสแผนที่ที่มีรูปแบบแผนที่เชื่อมโยงอยู่ โปรดดูคู่มือรหัสแผนที่เพื่อทําตามขั้นตอนต่อไปนี้

  1. สร้างรหัสแผนที่
  2. เชื่อมโยงรหัสแผนที่กับรูปแบบแผนที่

การเพิ่มรหัสแผนที่ลงในแอป

หากต้องการใช้รหัสแผนที่ที่คุณสร้างขึ้น ให้แก้ไขไฟล์ activity_main.xml และส่งรหัสแผนที่ในแอตทริบิวต์ map:mapId ของ SupportMapFragment

activity_main.xml

<fragment xmlns:map="http://schemas.android.com/apk/res-auto"
    class="com.google.android.gms.maps.SupportMapFragment"
    <!-- ... -->
    map:mapId="YOUR_MAP_ID" />

เมื่อเสร็จแล้ว ให้เรียกใช้แอปเพื่อดูแผนที่ในรูปแบบที่คุณเลือก

6. เพิ่มเครื่องหมาย

ในงานนี้ คุณต้องเพิ่มเครื่องหมายบนแผนที่ที่แสดงถึงจุดสนใจที่คุณต้องการไฮไลต์บนแผนที่ ก่อนอื่น ให้เรียกรายการสถานที่ที่คุณระบุไว้ในโปรเจ็กต์เริ่มต้น แล้วเพิ่มสถานที่เหล่านั้นลงในแผนที่ ในตัวอย่างนี้ รายการเหล่านี้คือร้านจักรยาน

bc5576877369b554.png

รับการอ้างอิงไปยัง GoogleMaps

ก่อนอื่น คุณต้องได้รับการอ้างอิงไปยังออบเจ็กต์ GoogleMap เพื่อให้คุณใช้วิธีการของออบเจ็กต์นั้นได้ โดยเพิ่มโค้ดต่อไปนี้ในเมธอด MainActivity.onCreate() ทันทีหลังจากเรียกไปที่ setContentView()

MainActivity.onCreate()

val mapFragment = supportFragmentManager.findFragmentById(   
    R.id.map_fragment
) as? SupportMapFragment
mapFragment?.getMapAsync { googleMap ->
    addMarkers(googleMap)
}

การติดตั้งใช้งานจะค้นหา SupportMapFragment ที่คุณเพิ่มในขั้นตอนก่อนหน้าก่อนโดยใช้เมธอด findFragmentById() ในออบเจ็กต์ SupportFragmentManager เมื่อได้รับข้อมูลอ้างอิงแล้ว ระบบจะเรียกใช้ getMapAsync() ก่อนโดยผ่านแลมบ์ดา แลมบ์ดานี้คือตําแหน่งที่ส่งผ่านวัตถุ GoogleMap ภายใน Labda นี้ ระบบจะเรียกใช้เมธอด addMarkers() ซึ่งกําหนดไว้ในไม่ช้า

คลาสที่ให้ไว้: PlacesReader

ในโปรเจ็กต์เริ่มต้น ระบบส่งชั้นเรียน PlacesReader ให้คุณแล้ว ชั้นเรียนนี้อ่านรายชื่อสถานที่ 49 แห่งที่จัดเก็บไว้ในไฟล์ JSON ชื่อ places.json และแสดงผลสถานที่เหล่านี้เป็น List<Place> สถานที่ดังกล่าวจะเป็นตัวแทนของร้านจักรยานรอบๆ ซานฟรานซิสโก แคลิฟอร์เนีย สหรัฐอเมริกา

หากมีข้อสงสัยเกี่ยวกับการใช้งานชั้นเรียนนี้ คุณจะเข้าถึงชั้นเรียนนี้ได้ใน GitHub หรือเปิดชั้นเรียน PlacesReader ใน Android Studio

ผู้อ่านสถานที่

package com.google.codelabs.buildyourfirstmap.place

import android.content.Context
import com.google.codelabs.buildyourfirstmap.R
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.InputStream
import java.io.InputStreamReader

/**
* Reads a list of place JSON objects from the file places.json
*/
class PlacesReader(private val context: Context) {

   // GSON object responsible for converting from JSON to a Place object
   private val gson = Gson()

   // InputStream representing places.json
   private val inputStream: InputStream
       get() = context.resources.openRawResource(R.raw.places)

   /**
    * Reads the list of place JSON objects in the file places.json
    * and returns a list of Place objects
    */
   fun read(): List<Place> {
       val itemType = object : TypeToken<List<PlaceResponse>>() {}.type
       val reader = InputStreamReader(inputStream)
       return gson.fromJson<List<PlaceResponse>>(reader, itemType).map {
           it.toPlace()
       }
   }

โหลดสถานที่

หากต้องการโหลดรายการร้านจักรยาน ให้เพิ่มพร็อพเพอร์ตี้ใน MainActivity ที่ชื่อ places และกําหนดดังนี้

MainActivity.places

private val places: List<Place> by lazy {
   PlacesReader(this).read()
}

โค้ดนี้จะเรียกใช้เมธอด read() ใน PlacesReader ซึ่งแสดงผล List<Place> Place มีพร็อพเพอร์ตี้ชื่อ name ชื่อสถานที่ และ latLng ซึ่งเป็นพิกัดของสถานที่

สถานที่

data class Place(
   val name: String,
   val latLng: LatLng,
   val address: LatLng,
   val rating: Float
)

เพิ่มเครื่องหมายลงในแผนที่

เมื่อรายการสถานที่โหลดเข้าสู่ความทรงจําแล้ว ขั้นตอนต่อไปคือการแสดงสถานที่เหล่านี้ในแผนที่

  1. สร้างเมธอดใน MainActivity ที่ชื่อว่า addMarkers() แล้วกําหนดวิธีต่อไปนี้

MainActivity.addMarkers()

/**
* Adds marker representations of the places list on the provided GoogleMap object
*/
private fun addMarkers(googleMap: GoogleMap) {
   places.forEach { place ->
       val marker = googleMap.addMarker(
           MarkerOptions()
               .title(place.name)
               .position(place.latLng)
       )
   }
}

วิธีนี้ทําซ้ําผ่านรายการ places ตามด้วยการเรียกใช้เมธอด addMarker() ในออบเจ็กต์ GoogleMap ที่ระบุ ตัวทําเครื่องหมายนี้สร้างขึ้นจากการสร้างออบเจ็กต์ MarkerOptions ซึ่งให้คุณปรับแต่งตัวทําเครื่องหมายเองได้ ในกรณีนี้ ให้ระบุตําแหน่งและตําแหน่งของเครื่องหมาย รวมทั้งชื่อร้านจักรยานและพิกัดตามลําดับ

  1. ไปที่แอปแล้วเดินไปที่ซานฟรานซิสโก เพื่อดูเครื่องหมายที่เพิ่งเพิ่มไป

7. ปรับแต่งเครื่องหมาย

มีตัวเลือกการปรับแต่งมากมายสําหรับเครื่องหมายที่คุณเพิ่งเพิ่มเพื่อช่วยให้โดดเด่นและแสดงข้อมูลที่เป็นประโยชน์แก่ผู้ใช้ ในงานนี้ คุณจะได้สํารวจเครื่องมือบางอย่างด้วยการปรับแต่งรูปภาพของเครื่องหมายแต่ละรายการ รวมถึงหน้าต่างข้อมูลที่แสดงเมื่อแตะเครื่องหมาย

a26f82802fe838e9.png

การเพิ่มหน้าต่างข้อมูล

โดยค่าเริ่มต้น หน้าต่างข้อมูลเมื่อคุณแตะเครื่องหมายจะแสดงชื่อและตัวอย่างข้อมูล (หากตั้งค่าไว้) คุณปรับแต่งเพื่อให้แสดงข้อมูลเพิ่มเติมได้ เช่น ที่อยู่และคะแนนของสถานที่

สร้างmark_info_contents.xml

ขั้นแรก ให้สร้างไฟล์เลย์เอาต์ใหม่ที่ชื่อ marker_info_contents.xml

  1. วิธีการคือ คลิกขวาที่โฟลเดอร์ app/src/main/res/layout ในมุมมองโปรเจ็กต์ใน Android Studio แล้วเลือก New > Layout Resource File

8cac51fcbef9171b.png

  1. ในกล่องโต้ตอบ ให้พิมพ์ marker_info_contents ในช่องชื่อไฟล์ และ LinearLayout ในช่อง Root element จากนั้นคลิกตกลง

8783af12baf07a80.png

หลังจากขยายไฟล์เลย์เอาต์นี้ ระบบก็จะแสดงเนื้อหานี้ในรูปแบบตัวแทนเนื้อหาภายในหน้าต่างข้อมูล

  1. คัดลอกเนื้อหาในข้อมูลโค้ดต่อไปนี้ ซึ่งจะเพิ่ม TextViews 3 รายการภายในกลุ่มมุมมอง LinearLayout แนวตั้ง และเขียนทับโค้ดเริ่มต้นในไฟล์

Mark_info_contents.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:tools="http://schemas.android.com/tools"
   android:orientation="vertical"
   android:layout_width="wrap_content"
   android:layout_height="wrap_content"
   android:gravity="center_horizontal"
   android:padding="8dp">

   <TextView
       android:id="@+id/text_view_title"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textColor="@android:color/black"
       android:textSize="18sp"
       android:textStyle="bold"
       tools:text="Title"/>

   <TextView
       android:id="@+id/text_view_address"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textColor="@android:color/black"
       android:textSize="16sp"
       tools:text="123 Main Street"/>

   <TextView
       android:id="@+id/text_view_rating"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:textColor="@android:color/black"
       android:textSize="16sp"
       tools:text="Rating: 3"/>

</LinearLayout>

สร้างการติดตั้งใช้งาน InfoWindowAdapter

หลังจากสร้างไฟล์เลย์เอาต์สําหรับหน้าต่างข้อมูลที่กําหนดเองแล้ว ขั้นตอนถัดไปคือการใช้อินเทอร์เฟซ GoogleMaps.InfoWindowAdapter อินเทอร์เฟซนี้มี 2 วิธี ได้แก่ getInfoWindow() และ getInfoContents() ทั้ง 2 วิธีนี้ส่งคืนออบเจ็กต์ View ที่ไม่บังคับ ซึ่งมีนโยบายเก่าใช้ปรับแต่งหน้าต่างเอง ขณะที่ตัวเลือกหลังคือการปรับแต่งเนื้อหา ในกรณีของคุณ ให้ใช้ทั้ง 2 อย่างและปรับแต่งการคืนสินค้าของ getInfoContents() ขณะแสดงผลเป็น Null ใน getInfoWindow() ซึ่งระบุว่าควรใช้หน้าต่างเริ่มต้น

  1. สร้างไฟล์ Kotlin ใหม่ชื่อ MarkerInfoWindowAdapter ในแพ็กเกจเดียวกับ MainActivity โดยคลิกขวาที่โฟลเดอร์ app/src/main/java/com/google/codelabs/buildyourfirstmap ในมุมมองโปรเจ็กต์ใน Android Studio จากนั้นเลือก New > Kotlin File/Class

3975ba36eba9f8e1.png

  1. ในกล่องโต้ตอบ ให้พิมพ์ MarkerInfoWindowAdapter และไฮไลต์ไฟล์ไว้

992235af53d3897f.png

  1. เมื่อสร้างไฟล์แล้ว ให้คัดลอกเนื้อหาในข้อมูลโค้ดต่อไปนี้ลงในไฟล์ใหม่

MarkerInfoWindowAdapter

import android.content.Context
import android.view.LayoutInflater
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.google.codelabs.buildyourfirstmap.place.Place

class MarkerInfoWindowAdapter(
    private val context: Context
) : GoogleMap.InfoWindowAdapter {
   override fun getInfoContents(marker: Marker?): View? {
       // 1. Get tag
       val place = marker?.tag as? Place ?: return null

       // 2. Inflate view and set title, address, and rating
       val view = LayoutInflater.from(context).inflate(
           R.layout.marker_info_contents, null
       )
       view.findViewById<TextView>(
           R.id.text_view_title
       ).text = place.name
       view.findViewById<TextView>(
           R.id.text_view_address
       ).text = place.address
       view.findViewById<TextView>(
           R.id.text_view_rating
       ).text = "Rating: %.2f".format(place.rating)

       return view
   }

   override fun getInfoWindow(marker: Marker?): View? {
       // Return null to indicate that the 
       // default window (white bubble) should be used
       return null
   }
}

ในเนื้อหาของเมธอด getInfoContents() ระบบจะสังเกตเครื่องหมายที่ระบุในเมธอดเป็นประเภท Place และหากแคสต์ไม่ได้ ระบบจะแสดงผลค่า Null (หากยังไม่ได้ตั้งค่าพร็อพเพอร์ตี้แท็กใน Marker แต่คุณจะทําในขั้นตอนถัดไป)

ถัดไป เลย์เอาต์ marker_info_contents.xml จะมีค่าสูงเกินจริงตามการตั้งค่าข้อความที่มี TextViews เป็นแท็ก Place

อัปเดตกิจกรรมหลัก

หากต้องการเลื่อนคอมโพเนนต์ทั้งหมดที่สร้างไว้ออกไป คุณจะต้องเพิ่มบรรทัด 2 บรรทัดใน MainActivity ชั้นเรียน

ขั้นแรก หากต้องการส่งผ่าน InfoWindowAdapter ที่กําหนดเองซึ่งเป็น MarkerInfoWindowAdapter ในการเรียกเมธอด getMapAsync ให้เรียกใช้เมธอด setInfoWindowAdapter() ในออบเจ็กต์ GoogleMap และสร้างอินสแตนซ์ใหม่ของ MarkerInfoWindowAdapter

  1. โดยเพิ่มโค้ดต่อไปนี้หลังจากเรียกเมธอด addMarkers() ใน getMapAsync() แลมบ์ดา

MainActivity.onCreate()

// Set custom info window adapter
googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))

และสุดท้าย คุณจะต้องตั้งค่าสถานที่แต่ละแห่งเป็นพร็อพเพอร์ตี้ของแท็กสําหรับเครื่องหมายทุกตัวที่เพิ่มลงในแผนที่

  1. โดยแก้ไขการเรียกใช้ places.forEach{} ในฟังก์ชัน addMarkers() ด้วยสิ่งต่อไปนี้

MainActivity.addMarkers()

places.forEach { place ->
   val marker = googleMap.addMarker(
       MarkerOptions()
           .title(place.name)
           .position(place.latLng)
           .icon(bicycleIcon)
   )

   // Set place as the tag on the marker object so it can be referenced within
   // MarkerInfoWindowAdapter
   marker.tag = place
}

เพิ่มรูปภาพตัวทําเครื่องหมายที่กําหนดเอง

การปรับแต่งภาพเครื่องหมายเป็นหนึ่งในวิธีสนุกๆ ในการสื่อสารประเภทตําแหน่งที่เครื่องหมายบนแผนที่ของคุณ ในขั้นตอนนี้ คุณจะแสดงจักรยานแทนเครื่องหมายสีแดงเริ่มต้นเพื่อแสดงร้านค้าแต่ละแห่งบนแผนที่ โปรเจ็กต์เริ่มต้นมีไอคอนจักรยาน ic_directions_bike_black_24dp.xml ใน app/src/res/drawable ที่คุณใช้

6eb7358bb61b0a88.png

ตั้งค่าบิตแมปที่กําหนดเองบนเครื่องหมาย

คุณสามารถใช้ไอคอนจักรยานที่วาดได้ขณะขับขี่ได้ ขั้นตอนถัดไปคือตั้งค่าไอคอนที่วาดได้ให้เป็นเครื่องหมายแต่ละรายการ' บนแผนที่ MarkerOptions มีเมธอด icon ซึ่งใช้ใน BitmapDescriptor ที่คุณใช้เพื่อให้บรรลุวัตถุประสงค์นี้

ก่อนอื่น คุณจะต้องแปลงเวกเตอร์ที่วาดได้ที่คุณเพิ่งเพิ่มลงใน BitmapDescriptor ไฟล์ชื่อ BitMapHelper ที่รวมอยู่ในโปรเจ็กต์เริ่มต้นมีฟังก์ชันผู้ช่วยที่เรียกว่า vectorToBitmap() ซึ่งจะทําหน้าที่ดังกล่าว

BitmapHelper

package com.google.codelabs.buildyourfirstmap

import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.util.Log
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory

object BitmapHelper {
   /**
    * Demonstrates converting a [Drawable] to a [BitmapDescriptor], 
    * for use as a marker icon. Taken from ApiDemos on GitHub:
    * https://github.com/googlemaps/android-samples/blob/main/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt
    */
   fun vectorToBitmap(
      context: Context,
      @DrawableRes id: Int, 
      @ColorInt color: Int
   ): BitmapDescriptor {
       val vectorDrawable = ResourcesCompat.getDrawable(context.resources, id, null)
       if (vectorDrawable == null) {
           Log.e("BitmapHelper", "Resource not found")
           return BitmapDescriptorFactory.defaultMarker()
       }
       val bitmap = Bitmap.createBitmap(
           vectorDrawable.intrinsicWidth,
           vectorDrawable.intrinsicHeight,
           Bitmap.Config.ARGB_8888
       )
       val canvas = Canvas(bitmap)
       vectorDrawable.setBounds(0, 0, canvas.width, canvas.height)
       DrawableCompat.setTint(vectorDrawable, color)
       vectorDrawable.draw(canvas)
       return BitmapDescriptorFactory.fromBitmap(bitmap)
   }
}

วิธีนี้ใช้ Context, รหัสทรัพยากรที่วาดได้ และจํานวนเต็มสี รวมถึงสร้างการนําเสนอ BitmapDescriptor ของวิธีดังกล่าว

ใช้วิธีการผู้ช่วยในการประกาศพร็อพเพอร์ตี้ใหม่ชื่อ bicycleIcon และให้คําจํากัดความต่อไปนี้ MainActivity.bicycleIcon

private val bicycleIcon: BitmapDescriptor by lazy {
   val color = ContextCompat.getColor(this, R.color.colorPrimary)
   BitmapHelper.vectorToBitmap(this, R.drawable.ic_directions_bike_black_24dp, color)
}

พร็อพเพอร์ตี้นี้ใช้สี colorPrimary ที่กําหนดไว้ล่วงหน้าในแอป และใช้แต้มนั้นเพื่อไฮไลต์ไอคอนจักรยานและคืนค่าเป็น BitmapDescriptor

  1. ในการใช้พร็อพเพอร์ตี้นี้ ให้เรียกใช้เมธอด icon ของ MarkerOptions ในเมธอด addMarkers() เพื่อกําหนดค่าไอคอนด้วยตนเอง เมื่อทําเช่นนี้ พร็อพเพอร์ตี้ตัวทําเครื่องหมายควรมีลักษณะดังนี้

MainActivity.addMarkers()

val marker = googleMap.addMarker(
    MarkerOptions()
        .title(place.name)
        .position(place.latLng)
        .icon(bicycleIcon)
)
  1. เรียกใช้แอปเพื่อดูเครื่องหมายที่อัปเดตแล้ว

8. ตัวทําเครื่องหมายคลัสเตอร์

คุณอาจสังเกตเห็นว่าเครื่องหมายที่คุณเพิ่มซ้อนทับกัน ทั้งนี้ขึ้นอยู่กับระยะทางที่คุณซูมเข้าไปในแผนที่ ตัวทําเครื่องหมายที่ทับซ้อนกันนั้นโต้ตอบได้ยากและสร้างเสียงรบกวนจํานวนมาก ซึ่งส่งผลกระทบต่อความสามารถในการใช้งานของแอป

68591edc86d73724.png

เพื่อปรับปรุงประสบการณ์ของผู้ใช้ให้ดียิ่งขึ้น เมื่อใดก็ตามที่คุณมีชุดข้อมูลขนาดใหญ่ที่รวมกันอยู่ใกล้กัน แนวทางปฏิบัติที่ดีที่สุดคือการนําคลัสเตอร์เครื่องหมายไปใช้ เมื่อใช้การจัดกลุ่ม เมื่อคุณซูมเข้าและออกในแผนที่ เครื่องหมายที่อยู่ในบริเวณใกล้เคียงจะกระจุกรวมกันอยู่เช่นนี้

f05e1ca27ff42bf6.png

ในการใช้งาน คุณต้องขอความช่วยเหลือจาก Maps SDK สําหรับไลบรารียูทิลิตีของ Android

Maps SDK สําหรับไลบรารียูทิลิตีของ Android

Maps SDK สําหรับ Android Utility Library สร้างขึ้นเป็นวิธีหนึ่งในการขยายฟังก์ชันการทํางานของ Maps SDK สําหรับ Android โดยมีฟีเจอร์ขั้นสูงต่างๆ เช่น คลัสเตอร์เครื่องหมาย ฮีตแมป KML และการรองรับ GeoJson การเข้ารหัสและถอดรหัส Polyline รวมถึงฟังก์ชันผู้ช่วยในรูปเรขาคณิต 360 องศาที่มีอยู่มากมาย

อัปเดต build.gradle

เนื่องจากไลบรารียูทิลิตีมีแพ็กเกจแยกต่างหากจาก Maps SDK สําหรับ Android คุณจึงต้องเพิ่มทรัพยากร Dependency ไปยังไฟล์ build.gradle

  1. อัปเดตส่วน dependencies ของไฟล์ app/build.gradle ได้เลย

build.gradle

implementation 'com.google.maps.android:android-maps-utils:1.1.0'
  1. เมื่อเพิ่มบรรทัดนี้ คุณจะต้องซิงค์โปรเจ็กต์เพื่อดึงข้อมูลทรัพยากร Dependency ใหม่

b7b030ec82c007fd.png

ใช้การจัดกลุ่ม

หากต้องการใช้การจัดกลุ่มในแอป ให้ทําตามขั้นตอน 3 ขั้นตอนต่อไปนี้

  1. ใช้อินเทอร์เฟซ ClusterItem
  2. คลาสย่อย DefaultClusterRenderer
  3. สร้าง ClusterManager แล้วเพิ่มรายการ

ใช้อินเทอร์เฟซ ClusterItem

ออบเจ็กต์ทั้งหมดที่แทนเครื่องหมายที่จัดกลุ่มได้บนแผนที่ต้องใช้อินเทอร์เฟซ ClusterItem ในกรณีของคุณ นั่นหมายความว่ารูปแบบ Place ต้องสอดคล้องกับ ClusterItem ดําเนินการต่อและเปิดไฟล์ Place.kt แล้วทําการแก้ไขต่อไปนี้

สถานที่

data class Place(
   val name: String,
   val latLng: LatLng,
   val address: String,
   val rating: Float
) : ClusterItem {
   override fun getPosition(): LatLng =
       latLng

   override fun getTitle(): String =
       name

   override fun getSnippet(): String =
       address
}

ClusterItem จะกําหนด 3 วิธีต่อไปนี้

  • getPosition() ซึ่งแสดงถึงสถานที่ LatLng
  • getTitle() ซึ่งแสดงถึงชื่อสถานที่
  • getSnippet() ซึ่งแสดงถึงที่อยู่ของสถานที่

คลาสย่อย DefaultClusterRenderer

ชั้นเรียนที่รับผิดชอบการใช้งานคลัสเตอร์ ClusterManager จะใช้คลาส ClusterRenderer ภายในเพื่อจัดการการสร้างคลัสเตอร์ขณะที่เลื่อนและซูมแผนที่ โดยค่าเริ่มต้นจะมาพร้อมกับตัวแสดงผลเริ่มต้น DefaultClusterRenderer ซึ่งใช้ ClusterRenderer สําหรับขั้นตอนง่ายๆ วิธีนี้น่าจะเพียงพอ อย่างไรก็ตาม เนื่องจากจําเป็นต้องปรับแต่งเครื่องหมาย คุณจึงต้องขยายคลาสนี้และเพิ่มการปรับแต่งในส่วนนี้

ดําเนินการต่อและสร้างไฟล์ Kotlin PlaceRenderer.kt ในแพ็กเกจ com.google.codelabs.buildyourfirstmap.place แล้วกําหนดสิ่งต่อไปนี้

PlaceRenderer

package com.google.codelabs.buildyourfirstmap.place

import android.content.Context
import androidx.core.content.ContextCompat
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import com.google.codelabs.buildyourfirstmap.BitmapHelper
import com.google.codelabs.buildyourfirstmap.R
import com.google.maps.android.clustering.ClusterManager
import com.google.maps.android.clustering.view.DefaultClusterRenderer

/**
* A custom cluster renderer for Place objects.
*/
class PlaceRenderer(
   private val context: Context,
   map: GoogleMap,
   clusterManager: ClusterManager<Place>
) : DefaultClusterRenderer<Place>(context, map, clusterManager) {

   /**
    * The icon to use for each cluster item
    */
   private val bicycleIcon: BitmapDescriptor by lazy {
       val color = ContextCompat.getColor(context,
           R.color.colorPrimary
       )
       BitmapHelper.vectorToBitmap(
           context,
           R.drawable.ic_directions_bike_black_24dp,
           color
       )
   }

   /**
    * Method called before the cluster item (the marker) is rendered.
    * This is where marker options should be set.
    */
   override fun onBeforeClusterItemRendered(
      item: Place,
      markerOptions: MarkerOptions
   ) {
       markerOptions.title(item.name)
           .position(item.latLng)
           .icon(bicycleIcon)
   }

   /**
    * Method called right after the cluster item (the marker) is rendered.
    * This is where properties for the Marker object should be set.
    */
   override fun onClusterItemRendered(clusterItem: Place, marker: Marker) {
       marker.tag = clusterItem
   }
}

คลาสนี้ลบล้างฟังก์ชัน 2 รายการต่อไปนี้

  • onBeforeClusterItemRendered() ซึ่งจะเรียกใช้ก่อนที่กลุ่มจะแสดงผลบนแผนที่ คุณระบุการปรับแต่งผ่าน MarkerOptions ได้ ซึ่งในกรณีนี้คือการตั้งค่าชื่อ ตําแหน่ง และไอคอนของเครื่องหมาย
  • onClusterItemRenderer() ซึ่งจะเรียกใช้ทันทีหลังจากแสดงเครื่องหมายบนแผนที่ นี่คือที่ที่คุณสามารถเข้าถึงออบเจ็กต์ Marker ที่สร้างขึ้น ซึ่งในกรณีนี้คือการตั้งค่าพร็อพเพอร์ตี้แท็กของเครื่องหมาย&#39

สร้าง ClusterManager และเพิ่มรายการ

สุดท้ายนี้ คุณจะต้องคลัสเตอร์ MainActivity เพื่อแสดงตัวอย่าง ClusterManager และให้ทรัพยากร Dependency ที่จําเป็นแก่คลัสเตอร์ ClusterManager จะจัดการการเพิ่มเครื่องหมาย (วัตถุ ClusterItem) ภายใน ดังนั้นแทนที่จะมอบหมายเครื่องหมายโดยตรงบนแผนที่ การมอบสิทธิ์นี้จึงมอบให้กับ ClusterManager นอกจากนี้ ClusterManager ยังเรียกใช้ setInfoWindowAdapter() ภายในด้วย ดังนั้นการตั้งค่าหน้าต่างข้อมูลที่กําหนดเองจะต้องดําเนินการบนออบเจ็กต์ MarkerManager.Collection ของ ClusterManger&#39

  1. หากต้องการเริ่มใช้งาน ให้แก้ไขเนื้อหาของแลมบ์ดาในการเรียก getMapAsync() ใน MainActivity.onCreate() ดําเนินการต่อและแสดงความคิดเห็นในการโทรหา addMarkers() และ setInfoWindowAdapter() และเรียกใช้เมธอดชื่อ addClusteredMarkers() ซึ่งคุณกําหนดถัดไป

MainActivity.onCreate()

mapFragment?.getMapAsync { googleMap ->
    //addMarkers(googleMap)
    addClusteredMarkers(googleMap)

    // Set custom info window adapter.
    // googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
}
  1. ต่อไปใน MainActivity ให้กําหนด addClusteredMarkers()

MainActivity.addClusteredMarkers()

/**
* Adds markers to the map with clustering support.
*/
private fun addClusteredMarkers(googleMap: GoogleMap) {
   // Create the ClusterManager class and set the custom renderer.
   val clusterManager = ClusterManager<Place>(this, googleMap)
   clusterManager.renderer =
       PlaceRenderer(
           this,
           googleMap,
           clusterManager
       )

   // Set custom info window adapter
   clusterManager.markerCollection.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))

   // Add the places to the ClusterManager.
   clusterManager.addItems(places)
   clusterManager.cluster()

   // Set ClusterManager as the OnCameraIdleListener so that it
   // can re-cluster when zooming in and out.
   googleMap.setOnCameraIdleListener {
       clusterManager.onCameraIdle()
   }
}

เมธอดนี้จะแจ้ง ClusterManager ผ่านตัวแสดงผลที่กําหนดเอง PlacesRenderer โดยเพิ่มสถานที่ทั้งหมด และเรียกใช้เมธอด cluster() นอกจากนี้ เนื่องจาก ClusterManager ใช้วิธีการของ setInfoWindowAdapter() ในออบเจ็กต์แผนที่ ดังนั้นการตั้งค่าหน้าต่างข้อมูลที่กําหนดเองจึงต้องทําในออบเจ็กต์ ClusterManager.markerCollection ข้อสุดท้าย เนื่องจากคุณต้องการให้คลัสเตอร์เปลี่ยนแปลงขณะที่ผู้ใช้เลื่อนและซูมรอบๆ แผนที่ จึงให้ OnCameraIdleListener แก่ googleMap ดังนั้นเมื่อกล้องไม่มีการใช้งาน clusterManager.onCameraIdle() จะเรียกใช้

  1. ดําเนินการต่อและเรียกใช้แอปเพื่อดูร้านค้าใหม่ในคลัสเตอร์

9. วาดในแผนที่

ขณะที่คุณสํารวจวิธีการวาดบนแผนที่ (โดยการเพิ่มเครื่องหมาย) แล้ว Maps Maps สําหรับ Android จะรองรับวิธีการอื่นๆ ที่คุณวาดได้เพื่อแสดงข้อมูลที่เป็นประโยชน์บนแผนที่

ตัวอย่างเช่น หากคุณต้องการแสดงเส้นทางและพื้นที่บนแผนที่ คุณสามารถใช้รูปหลายเหลี่ยมและรูปหลายเหลี่ยมเพื่อแสดงข้อมูลเหล่านี้บนแผนที่ หรือหากต้องการแก้ไขรูปภาพในแพลตฟอร์มของพื้นดิน คุณใช้การวางซ้อนพื้นได้

ในงานนี้ คุณจะได้ดูวิธีวาดรูปร่าง โดยเฉพาะวงกลมรอบเครื่องหมายต่างๆ เมื่อแตะ

f98ce13055430352.png

เพิ่ม Listener การคลิก

โดยปกติแล้ว วิธีเพิ่ม Listener การคลิกให้กับตัวทําเครื่องหมายคือการส่งผ่าน Listener การคลิกไปยังออบเจ็กต์ GoogleMap โดยตรงผ่าน setOnMarkerClickListener() อย่างไรก็ตาม เนื่องจากคุณกําลังใช้คลัสเตอร์ คุณจึงต้องฟัง Listener การคลิกไปยัง ClusterManager แทน

  1. ในเมธอด addClusteredMarkers() ใน MainActivity ให้เพิ่มบรรทัดต่อไปนี้หลังจากส่งคําขอไปยัง cluster() แล้ว

MainActivity.addClusteredMarkers()

// Show polygon
clusterManager.setOnClusterItemClickListener { item ->
   addCircle(googleMap, item)
   return@setOnClusterItemClickListener false
}

เมธอดนี้จะเพิ่ม Listener และเรียกใช้เมธอด addCircle() ซึ่งคุณจะกําหนดถัดไป ท้ายที่สุดแล้ว false จะแสดงผลจากวิธีนี้เพื่อบ่งบอกว่าวิธีการนี้ไม่ได้ใช้เหตุการณ์นี้

  1. ต่อไปคุณต้องกําหนดพร็อพเพอร์ตี้ circle และวิธี addCircle() ใน MainActivity

MainActivity.addCircle()

private var circle: Circle? = null

/**
* Adds a [Circle] around the provided [item]
*/
private fun addCircle(googleMap: GoogleMap, item: Place) {
   circle?.remove()
   circle = googleMap.addCircle(
       CircleOptions()
           .center(item.latLng)
           .radius(1000.0)
           .fillColor(ContextCompat.getColor(this, R.color.colorPrimaryTranslucent))
           .strokeColor(ContextCompat.getColor(this, R.color.colorPrimary))
   )
}

ระบบได้ตั้งค่าพร็อพเพอร์ตี้ circle แล้ว เมื่อใดก็ตามที่แตะเครื่องหมายใหม่ วงกลมก่อนหน้าจะถูกนําออกและเพิ่มวงกลมใหม่ลงไป โปรดสังเกตว่า API สําหรับการเพิ่มวงกลมจะคล้ายกับการเพิ่มเครื่องหมาย

  1. ดําเนินการต่อและเรียกใช้แอปเพื่อดูการเปลี่ยนแปลง

10. การควบคุมกล้อง

ในขั้นตอนสุดท้าย คุณจะดูการควบคุมกล้องบางส่วนเพื่อให้คุณสามารถโฟกัสมุมมองรอบๆ บางภูมิภาคได้

กล้องและมุมมอง

หากคุณสังเกตเห็นว่าแอปทํางานอยู่ กล้องถ่ายรูปจะแสดงทวีปแอฟริกา และต้องเลื่อนและซูมไปยังซานฟรานซิสโกอย่างน่าอัศจรรย์เพื่อค้นหาเครื่องหมายที่คุณเพิ่มไว้ แม้ว่าจะเป็นวิธีที่สนุกสนานในการสํารวจโลก แต่เครื่องมือนี้มีประโยชน์หากคุณต้องการแสดงเครื่องหมายโดยทันที

ในการช่วยนี้ คุณสามารถตั้งค่าตําแหน่งของกล้องแบบเป็นโปรแกรมเพื่อให้มุมมองอยู่ตรงกลางในตําแหน่งที่ต้องการ

  1. ดําเนินการต่อและเพิ่มโค้ดต่อไปนี้ลงในการเรียก getMapAsync() เพื่อปรับมุมมองกล้องเพื่อให้เริ่มต้นในซานฟรานซิสโกเมื่อมีการเปิดแอป

MainActivity.onCreate()

mapFragment?.getMapAsync { googleMap ->
   // Ensure all places are visible in the map.
   googleMap.setOnMapLoadedCallback {
       val bounds = LatLngBounds.builder()
       places.forEach { bounds.include(it.latLng) }
       googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 20))
   }
}

ก่อนอื่นให้เรียกใช้ setOnMapLoadedCallback() เพื่อให้การอัปเดตกล้องดําเนินการหลังจากที่โหลดแผนที่แล้วเท่านั้น ขั้นตอนนี้เป็นสิ่งจําเป็นเนื่องจากจําเป็นต้องคํานวณคุณสมบัติของแผนที่ เช่น มิติข้อมูล ก่อนที่จะเรียกใช้การอัปเดตกล้องถ่ายรูป

ในแลมบ์ดา ออบเจ็กต์ LatLngBounds ใหม่จะสร้างขึ้นซึ่งจะกําหนดพื้นที่รูปสี่เหลี่ยมผืนผ้าบนแผนที่ ค่านี้ค่อยๆ เพิ่มโดยการรวมค่าสถานที่ LatLng ทั้งหมดไว้ในค่านั้นเพื่อให้แน่ใจว่าสถานที่ทั้งหมดอยู่ภายในขอบเขต เมื่อสร้างออบเจ็กต์นี้แล้ว ระบบจะเรียกใช้เมธอด moveCamera() ใน GoogleMap และมีการระบุ CameraUpdate ผ่าน CameraUpdateFactory.newLatLngBounds(bounds.build(), 20)

  1. เรียกใช้แอปและตรวจสอบว่ากล้องเริ่มต้นอยู่ในซานฟรานซิสโกแล้ว

กําลังฟังการเปลี่ยนแปลงจากกล้อง

นอกจากการปรับเปลี่ยนตําแหน่งกล้องแล้ว คุณยังฟังการอัปเดตของกล้องขณะที่ผู้ใช้เคลื่อนที่ไปรอบๆ แผนที่ได้ด้วย วิธีนี้อาจมีประโยชน์หากคุณต้องการแก้ไข UI เมื่อมีการขยับกล้อง

เพื่อความสนุกสนาน คุณแก้ไขโค้ดเพื่อให้เครื่องหมายโปร่งแสงทุกครั้งที่มีการย้ายกล้อง

  1. ในเมธอด addClusteredMarkers() ให้เพิ่มบรรทัดต่อไปนี้ที่ด้านล่างของเมธอด

MainActivity.addClusteredMarkers()

// When the camera starts moving, change the alpha value of the marker to translucent.
googleMap.setOnCameraMoveStartedListener {
   clusterManager.markerCollection.markers.forEach { it.alpha = 0.3f }
   clusterManager.clusterMarkerCollection.markers.forEach { it.alpha = 0.3f }
}

การดําเนินการนี้จะเพิ่ม OnCameraMoveStartedListener เพื่อที่เมื่อใดก็ตามที่กล้องเริ่มเคลื่อนที่ ค่าเครื่องหมายทั้งหมด&#39 (ทั้งคลัสเตอร์และเครื่องหมาย) จะปรับเปลี่ยนเป็น 0.3f เพื่อให้ตัวทําเครื่องหมายโปร่งแสง

  1. สุดท้าย หากต้องการแก้ไขตัวทําเครื่องหมายโปร่งแสงให้กลับมาทึบเมื่อกล้องหยุด ให้แก้ไขเนื้อหาของ setOnCameraIdleListener ในเมธอด addClusteredMarkers() เป็นสิ่งต่อไปนี้

MainActivity.addClusteredMarkers()

googleMap.setOnCameraIdleListener {
   // When the camera stops moving, change the alpha value back to opaque.
   clusterManager.markerCollection.markers.forEach { it.alpha = 1.0f }
   clusterManager.clusterMarkerCollection.markers.forEach { it.alpha = 1.0f }

   // Call clusterManager.onCameraIdle() when the camera stops moving so that reclustering
   // can be performed when the camera stops moving.
   clusterManager.onCameraIdle()
}
  1. เริ่มเรียกใช้แอปเพื่อดูผลลัพธ์ได้เลย

11. แผนที่ KTX

สําหรับแอป Kotlin ที่ใช้ SDK สําหรับ Google Maps Platform สําหรับ Android ตั้งแต่ 1 รายการขึ้นไป ส่วนขยายของ Kotlin หรือไลบรารี KTX จะช่วยให้คุณใช้ประโยชน์จากฟีเจอร์ของภาษา Kotlin เช่น โครูทีน พร็อพเพอร์ตี้/ฟังก์ชันส่วนขยาย และอื่นๆ อีกมากมาย Google Maps SDK แต่ละรายการมีไลบรารี KTX ที่เกี่ยวข้องดังที่แสดงด้านล่าง

แผนภาพ KTX ของ Google Maps Platform

ในงานนี้ คุณจะได้ใช้ไลบรารี Maps KTX และ Maps Utils KTX ในแอปของคุณและปรับเปลี่ยนงานก่อนหน้านี้&#39 ติดตั้งใช้งานเพื่อให้คุณใช้ฟีเจอร์ภาษาเฉพาะสําหรับ Kotlin ในแอปได้

  1. รวมทรัพยากร Dependency ของ KTX ในไฟล์ build.gradle ระดับแอป

เนื่องจากแอปใช้ทั้ง Maps SDK สําหรับ Android และ Maps SDK สําหรับ Android Utility Library คุณจะต้องรวมไลบรารี KTX ที่เกี่ยวข้องสําหรับไลบรารีเหล่านี้ด้วย คุณจะใช้ฟีเจอร์ที่พบในไลบรารี AndroidX Lifecycle KTX ในงานนี้ด้วย ดังนั้นโปรดรวมทรัพยากร Dependency ดังกล่าวไว้ในไฟล์ build.gradle ระดับแอปด้วย

build.gradle

dependencies {
    // ...

    // Maps SDK for Android KTX Library
    implementation 'com.google.maps.android:maps-ktx:3.0.0'

    // Maps SDK for Android Utility Library KTX Library
    implementation 'com.google.maps.android:maps-utils-ktx:3.0.0'

    // Lifecycle Runtime KTX Library
    implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
}
  1. ใช้ฟังก์ชันส่วนขยายของ GoogleMaps.addMarker() และ GoogleMAP.addCircle()

ไลบรารี Maps KTX เป็นทางเลือกของ API แบบ DSL สําหรับ GoogleMap.addMarker(MarkerOptions) และ GoogleMap.addCircle(CircleOptions) ที่ใช้ในขั้นตอนก่อนหน้า หากต้องการใช้ API ที่กล่าวมาข้างต้น การสร้างชั้นเรียนที่มีตัวเลือกสําหรับเครื่องหมายหรือวงกลมนั้นเป็นสิ่งที่จําเป็น ในขณะที่ทางเลือกแบบ KTX ทําให้คุณสามารถตั้งค่าตัวเลือกเครื่องหมายหรือวงกลมในแลมบ์ดาที่คุณให้ไว้ได้

หากต้องการใช้ API เหล่านี้ ให้อัปเดตเมธอด MainActivity.addMarkers(GoogleMap) และ MainActivity.addCircle(GoogleMap)

MainActivity.addMarkers(GoogleMaps)

/**
 * Adds markers to the map. These markers won't be clustered.
 */
private fun addMarkers(googleMap: GoogleMap) {
    places.forEach { place ->
        val marker = googleMap.addMarker {
            title(place.name)
            position(place.latLng)
            icon(bicycleIcon)
        }
        // Set place as the tag on the marker object so it can be referenced within
        // MarkerInfoWindowAdapter
        marker.tag = place
    }
}

MainActivity.addCircle(GoogleMaps)

/**
 * Adds a [Circle] around the provided [item]
 */
private fun addCircle(googleMap: GoogleMap, item: Place) {
    circle?.remove()
    circle = googleMap.addCircle {
        center(item.latLng)
        radius(1000.0)
        fillColor(ContextCompat.getColor(this@MainActivity, R.color.colorPrimaryTranslucent))
        strokeColor(ContextCompat.getColor(this@MainActivity, R.color.colorPrimary))
    }
}

การเขียนเมธอดข้างต้นด้วยวิธีนี้จะช่วยให้อ่านได้ละเอียดขึ้นมากด้วยการใช้ฟังก์ชันลิเทอรัลที่มีตัวรับสัญญาณ จาก Kotlin

  1. ใช้ฟังก์ชันการระงับส่วนขยาย Support MapFragment.aWait Map() และ GoogleMAP.aWait MapLoad()

ไลบรารี Maps KTX มีส่วนขยายฟังก์ชันสําหรับระงับที่ใช้ร่วมกับโครูทีนด้วย นั่นคือมีการระงับฟังก์ชันสํารองสําหรับ SupportMapFragment.getMapAsync(OnMapReadyCallback) และ GoogleMap.setOnMapLoadedCallback(OnMapLoadedCallback) การใช้ API ทางเลือกเหล่านี้ทําให้ไม่จําเป็นต้องส่งโค้ดเรียกกลับและช่วยให้คุณได้รับการตอบสนองของเมธอดเหล่านี้แบบอนุกรมและซิงโครนัสแทน

เนื่องจากวิธีการเหล่านี้จะระงับฟังก์ชัน การใช้งานจึงต้องเกิดขึ้นภายในโครูทีน ไลบรารี Lifecycle Runtime KTX มีส่วนขยายที่ให้ขอบเขต Coroutine ที่คํานึงถึงวงจรการใช้งานเพื่อให้ Coroutine ทํางานและหยุดที่เหตุการณ์ในวงจรที่เหมาะสม

เมื่อรวมแนวคิดเหล่านี้ ให้อัปเดตเมธอด MainActivity.onCreate(Bundle):

MainActivity.onCreate(แพ็กเกจ)

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val mapFragment =
        supportFragmentManager.findFragmentById(R.id.map_fragment) as SupportMapFragment
    lifecycleScope.launchWhenCreated {
        // Get map
        val googleMap = mapFragment.awaitMap()

        // Wait for map to finish loading
        googleMap.awaitMapLoad()

        // Ensure all places are visible in the map
        val bounds = LatLngBounds.builder()
        places.forEach { bounds.include(it.latLng) }
        googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 20))

        addClusteredMarkers(googleMap)
    }
}

ขอบเขต lifecycleScope.launchWhenCreated โครูทีนจะเรียกใช้การบล็อกเมื่อกิจกรรมอยู่ในสถานะที่สร้างเป็นอย่างน้อย โปรดสังเกตว่าการเรียกเพื่อเรียกออบเจ็กต์ GoogleMap และเพื่อรอให้แผนที่โหลดเสร็จจึงแทนที่ด้วย SupportMapFragment.awaitMap() และ GoogleMap.awaitMapLoad() ตามลําดับ การถอดรหัสโค้ดใหม่โดยใช้ฟังก์ชันการระงับเหล่านี้ช่วยให้คุณเขียนโค้ดตามโค้ดเรียกกลับที่เทียบเท่าได้ตามลําดับ

  1. เริ่มสร้างแอปอีกครั้งด้วยการเปลี่ยนแปลงของคุณ

12. ยินดีด้วย

ยินดีด้วย คุณได้อธิบายเนื้อหาไว้มากมายและหวังว่าคุณจะเข้าใจฟีเจอร์หลักที่มีใน Maps SDK สําหรับ Android ได้ดียิ่งขึ้น

ดูข้อมูลเพิ่มเติม

  • SDK สถานที่สําหรับ Android สํารวจชุดข้อมูลสถานที่ที่สมบูรณ์เพื่อค้นพบธุรกิจรอบตัวคุณ
  • android-maps-ktx เป็นไลบรารีแบบโอเพนซอร์สที่ให้คุณผสานรวมกับ Maps SDK สําหรับ Android และไลบรารี SDK ของ Maps สําหรับ Android Utility Library ในรูปแบบที่ใช้งานร่วมกับ Kotlin ได้
  • android-place-ktx - ไลบรารีโอเพนซอร์สที่ให้คุณผสานรวมกับ Places SDK สําหรับ Android ด้วยวิธีที่เหมาะกับ Kotlin
  • android-samples - โค้ดตัวอย่างใน GitHub ที่แสดงฟีเจอร์ทั้งหมดที่ครอบคลุมใน Codelab นี้และอื่นๆ
  • Kotlin Codelabs เพิ่มเติมสําหรับการสร้างแอป Android ด้วย Google Maps Platform