1. ก่อนที่คุณจะเริ่มต้น
โค้ดแล็บนี้จะสอนวิธีผสานรวม Maps SDK สำหรับ Android กับแอปและใช้ฟีเจอร์หลักของ SDK โดยการสร้างแอปที่แสดงแผนที่ร้านจักรยานในซานฟรานซิสโก รัฐแคลิฟอร์เนีย สหรัฐอเมริกา
ข้อกำหนดเบื้องต้น
- ความรู้พื้นฐานเกี่ยวกับการพัฒนา Kotlin และ Android
สิ่งที่คุณต้องดำเนินการ
- เปิดใช้และใช้ Maps SDK สำหรับ Android เพื่อเพิ่ม Google Maps ลงในแอป Android
- เพิ่ม ปรับแต่ง และจัดกลุ่มเครื่องหมาย
- วาดเส้นและรูปหลายเหลี่ยมบนแผนที่
- ควบคุมมุมมองของกล้องโดยใช้โปรแกรม
สิ่งที่คุณต้องมี
- Maps SDK สำหรับ Android
- บัญชี Google ที่เปิดใช้การเรียกเก็บเงิน
- Android Studio 2020.3.1 ขึ้นไป
- บริการ Google Play ที่ติดตั้งใน Android Studio
- อุปกรณ์ Android หรือโปรแกรมจำลอง Android ที่ใช้แพลตฟอร์ม Google APIs ที่อิงตาม Android 4.2.2 ขึ้นไป (ดูขั้นตอนการติดตั้งได้ที่เรียกใช้แอปในโปรแกรมจำลอง Android)
2. ตั้งค่า
สำหรับขั้นตอนการเปิดใช้ต่อไปนี้ คุณต้องเปิดใช้ Maps SDK สำหรับ Android
ตั้งค่า Google Maps Platform
หากยังไม่มีบัญชี Google Cloud Platform และโปรเจ็กต์ที่เปิดใช้การเรียกเก็บเงิน โปรดดูคู่มือเริ่มต้นใช้งาน Google Maps Platform เพื่อสร้างบัญชีสำหรับการเรียกเก็บเงินและโปรเจ็กต์
- ใน Cloud Console ให้คลิกเมนูแบบเลื่อนลงของโปรเจ็กต์ แล้วเลือกโปรเจ็กต์ที่ต้องการใช้สำหรับ Codelab นี้
- เปิดใช้ Google Maps Platform APIs และ SDK ที่จำเป็นสำหรับ Codelab นี้ใน Google Cloud Marketplace โดยทำตามขั้นตอนในวิดีโอนี้หรือเอกสารประกอบนี้
- สร้างคีย์ API ในหน้าข้อมูลเข้าสู่ระบบของ Cloud Console คุณสามารถทำตามขั้นตอนในวิดีโอนี้หรือเอกสารประกอบนี้ คำขอทั้งหมดไปยัง Google Maps Platform ต้องใช้คีย์ API
3. การเริ่มใช้งานอย่างง่าย
เรามีโค้ดเริ่มต้นที่จะช่วยให้คุณเริ่มต้นใช้งานได้อย่างรวดเร็วที่สุด และช่วยให้คุณทำตาม Codelab นี้ได้ คุณสามารถข้ามไปยังโซลูชันได้ แต่หากต้องการทำตามขั้นตอนทั้งหมดเพื่อสร้างโซลูชันด้วยตนเอง โปรดอ่านต่อ
- โคลนที่เก็บหากคุณติดตั้ง
git
ไว้
git clone https://github.com/googlecodelabs/maps-platform-101-android.git
หรือจะคลิกปุ่มต่อไปนี้เพื่อดาวน์โหลดซอร์สโค้ดก็ได้
- เมื่อได้รับโค้ดแล้ว ให้เปิดโปรเจ็กต์ที่อยู่ในไดเรกทอรี
starter
ใน Android Studio
4. เพิ่ม Google Maps
ในส่วนนี้ คุณจะเพิ่ม Google Maps เพื่อให้โหลดเมื่อเปิดแอป
เพิ่มคีย์ API
คุณต้องระบุคีย์ API ที่สร้างไว้ในขั้นตอนก่อนหน้าให้กับแอป เพื่อให้ Maps SDK สำหรับ Android เชื่อมโยงคีย์กับแอปได้
- หากต้องการระบุข้อมูลนี้ ให้เปิดไฟล์ที่ชื่อ
local.properties
ในไดเรกทอรีรากของโปรเจ็กต์ (ระดับเดียวกับgradle.properties
และsettings.gradle
) - ในไฟล์นั้น ให้กำหนดคีย์ใหม่
GOOGLE_MAPS_API_KEY
โดยมีค่าเป็นคีย์ API ที่คุณสร้างขึ้น
local.properties
GOOGLE_MAPS_API_KEY=YOUR_KEY_HERE
โปรดทราบว่า local.properties
แสดงอยู่ในไฟล์ .gitignore
ในที่เก็บ Git เนื่องจากระบบถือว่าคีย์ API เป็นข้อมูลที่ละเอียดอ่อนและไม่ควรเช็คอินไปยังการควบคุมแหล่งที่มา หากเป็นไปได้
- จากนั้น หากต้องการเปิดเผย API เพื่อให้ใช้ได้ทั่วทั้งแอป ให้รวมปลั๊กอิน Secrets Gradle Plugin for Android ไว้ในไฟล์
build.gradle
ของแอปซึ่งอยู่ในไดเรกทอรีapp/
และเพิ่มบรรทัดต่อไปนี้ภายในบล็อกplugins
build.gradle ระดับแอป
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
นอกจากนี้ คุณยังต้องแก้ไขไฟล์ build.gradle
ระดับโปรเจ็กต์เพื่อให้มี classpath ต่อไปนี้ด้วย
build.gradle ระดับโปรเจ็กต์
buildscript {
dependencies {
// ...
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:1.3.0"
}
}
ปลั๊กอินนี้จะทำให้คีย์ที่คุณกำหนดไว้ในไฟล์ local.properties
พร้อมใช้งานเป็นตัวแปรบิลด์ในไฟล์ Android Manifest และเป็นตัวแปรในคลาส BuildConfig
ที่ Gradle สร้างขึ้นในเวลาบิลด์ การใช้ปลั๊กอินนี้จะนำโค้ดบอยเลอร์เพลตที่จำเป็นต่อการอ่านพร็อพเพอร์ตี้จาก local.properties
ออก เพื่อให้เข้าถึงได้ทั่วทั้งแอป
เพิ่มการอ้างอิง Google Maps
- ตอนนี้คุณเข้าถึงคีย์ API ภายในแอปได้แล้ว ขั้นตอนถัดไปคือการเพิ่มทรัพยากร Dependency ของ Maps SDK สำหรับ Android ลงในไฟล์
build.gradle
ของแอป
ในโปรเจ็กต์เริ่มต้นที่มาพร้อมกับโค้ดแล็บนี้ ระบบได้เพิ่มการอ้างอิงนี้ให้คุณแล้ว
build.gradle
dependencies {
// Dependency to include Maps SDK for Android
implementation 'com.google.android.gms:play-services-maps:17.0.0'
}
- จากนั้นเพิ่มแท็ก
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}" />
- จากนั้นสร้างไฟล์เลย์เอาต์ใหม่ชื่อ
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
พื้นฐานที่คุณใช้ในขั้นตอนต่อๆ ไป
- สุดท้าย ให้อัปเดตคลาส
MainActivity
ที่อยู่ในapp/src/main/java/com/google/codelabs/buildyourfirstmap
โดยเพิ่มโค้ดต่อไปนี้เพื่อลบล้างเมธอดonCreate
เพื่อให้คุณตั้งค่าเนื้อหาด้วยเลย์เอาต์ใหม่ที่เพิ่งสร้างได้
MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
- ตอนนี้ให้เรียกใช้แอป คุณควรเห็นแผนที่โหลดบนหน้าจอของอุปกรณ์
5. การจัดรูปแบบแผนที่ในระบบคลาวด์ (ไม่บังคับ)
คุณปรับแต่งสไตล์ของแผนที่ได้โดยใช้การจัดรูปแบบแผนที่ในระบบคลาวด์
สร้างรหัสแผนที่
หากยังไม่ได้สร้างรหัสแมปที่มีรูปแบบแผนที่เชื่อมโยงอยู่ โปรดดูคำแนะนำเกี่ยวกับรหัสแมปเพื่อทำตามขั้นตอนต่อไปนี้
- สร้างรหัสแผนที่
- เชื่อมโยงรหัสแผนที่กับรูปแบบแผนที่
การเพิ่มรหัสแมปลงในแอป
หากต้องการใช้รหัสแผนที่ที่สร้างขึ้น ให้แก้ไขไฟล์ 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. เพิ่มเครื่องหมาย
ในงานนี้ คุณจะเพิ่มเครื่องหมายลงในแผนที่ซึ่งแสดงจุดที่น่าสนใจที่ต้องการไฮไลต์บนแผนที่ ก่อนอื่น ให้ดึงรายการสถานที่ที่ระบุไว้ในโปรเจ็กต์เริ่มต้นสำหรับคุณ จากนั้นเพิ่มสถานที่เหล่านั้นลงในแผนที่ ในตัวอย่างนี้คือร้านจักรยาน
รับการอ้างอิงไปยัง GoogleMap
ก่อนอื่น คุณต้องรับข้อมูลอ้างอิงไปยังออบเจ็กต์ 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()
ตามด้วยการส่งผ่านใน Lambda Lambda นี้คือที่ที่ส่งผ่านออบเจ็กต์ GoogleMap
ภายใน Lambda นี้ ระบบจะเรียกใช้การเรียกเมธอด addMarkers()
ซึ่งจะกำหนดในไม่ช้า
ชั้นเรียนที่จัดให้: PlacesReader
ในโปรเจ็กต์เริ่มต้น เราได้จัดเตรียมคลาส PlacesReader
ไว้ให้คุณแล้ว คลาสนี้จะอ่านรายการสถานที่ 49 แห่งที่จัดเก็บไว้ในไฟล์ JSON ชื่อ places.json
และแสดงผลเป็น List<Place>
สถานที่ต่างๆ แสดงรายการร้านจักรยานรอบๆ ซานฟรานซิสโก รัฐแคลิฟอร์เนีย สหรัฐอเมริกา
หากสนใจการใช้งานคลาสนี้ คุณสามารถเข้าถึงได้ใน GitHub หรือเปิดคลาส PlacesReader
ใน Android Studio
PlacesReader
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
)
เพิ่มเครื่องหมายลงในแผนที่
เมื่อโหลดรายการสถานที่ลงในหน่วยความจำแล้ว ขั้นตอนถัดไปคือการแสดงสถานที่เหล่านี้บนแผนที่
- สร้างเมธอดใน
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
ซึ่งช่วยให้คุณปรับแต่งเครื่องหมายได้ ในกรณีนี้ ระบบจะระบุชื่อและตำแหน่งของเครื่องหมาย ซึ่งแสดงถึงชื่อร้านจักรยานและพิกัดตามลำดับ
- เรียกใช้แอป แล้วไปที่ซานฟรานซิสโกเพื่อดูเครื่องหมายที่คุณเพิ่งเพิ่ม
7. ปรับแต่งเครื่องหมาย
มีตัวเลือกการปรับแต่งหลายอย่างสำหรับเครื่องหมายที่คุณเพิ่งเพิ่มเพื่อช่วยให้เครื่องหมายโดดเด่นและสื่อข้อมูลที่เป็นประโยชน์แก่ผู้ใช้ ในงานนี้ คุณจะได้สำรวจเครื่องหมายบางส่วนโดยการปรับแต่งรูปภาพของเครื่องหมายแต่ละรายการ รวมถึงหน้าต่างข้อมูลที่แสดงเมื่อแตะเครื่องหมาย
การเพิ่มหน้าต่างข้อมูล
โดยค่าเริ่มต้น หน้าต่างข้อมูลจะแสดงชื่อและข้อมูลโค้ดของเครื่องหมาย (หากตั้งค่าไว้) เมื่อคุณแตะเครื่องหมาย คุณปรับแต่งส่วนนี้เพื่อให้แสดงข้อมูลเพิ่มเติมได้ เช่น ที่อยู่และคะแนนของสถานที่
สร้าง marker_info_contents.xml
ก่อนอื่นให้สร้างไฟล์เลย์เอาต์ใหม่ชื่อ marker_info_contents.xml
- โดยคลิกขวาที่โฟลเดอร์
app/src/main/res/layout
ในมุมมองโปรเจ็กต์ใน Android Studio แล้วเลือกใหม่ > ไฟล์ทรัพยากรเลย์เอาต์
- ในกล่องโต้ตอบ ให้พิมพ์
marker_info_contents
ในช่องชื่อไฟล์ และLinearLayout
ในช่องRoot element
จากนั้นคลิกตกลง
ต่อมาจะขยายไฟล์เลย์เอาต์นี้เพื่อแสดงเนื้อหาภายในหน้าต่างข้อมูล
- คัดลอกเนื้อหาในข้อมูลโค้ดต่อไปนี้ ซึ่งจะเพิ่ม
TextViews
3 รายการภายในกลุ่มมุมมองแนวตั้งLinearLayout
แล้วเขียนทับโค้ดเริ่มต้นในไฟล์
marker_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
หลังจากสร้างไฟล์เลย์เอาต์สำหรับหน้าต่างข้อมูลที่กำหนดเองแล้ว ขั้นตอนถัดไปคือการใช้ส่วนติดต่อ GoogleMap.InfoWindowAdapter อินเทอร์เฟซนี้มี 2 วิธี ได้แก่ getInfoWindow()
และ getInfoContents()
ทั้ง 2 วิธีจะแสดงผลออบเจ็กต์ View
ที่ไม่บังคับ โดยวิธีแรกใช้เพื่อปรับแต่งหน้าต่างเอง ส่วนวิธีที่ 2 ใช้เพื่อปรับแต่งเนื้อหาของหน้าต่าง ในกรณีของคุณ คุณจะใช้ทั้ง 2 อย่างและปรับแต่งการคืนค่าของ getInfoContents()
ขณะที่คืนค่าเป็น Null ใน getInfoWindow()
ซึ่งบ่งบอกว่าควรใช้ช่วงเริ่มต้น
- สร้างไฟล์ Kotlin ใหม่ชื่อ
MarkerInfoWindowAdapter
ในแพ็กเกจเดียวกับMainActivity
โดยคลิกขวาที่โฟลเดอร์app/src/main/java/com/google/codelabs/buildyourfirstmap
ในมุมมองโปรเจ็กต์ใน Android Studio จากนั้นเลือกใหม่ > ไฟล์/คลาส Kotlin
- ในกล่องโต้ตอบ ให้พิมพ์
MarkerInfoWindowAdapter
และไฮไลต์ไฟล์ไว้
- เมื่อสร้างไฟล์แล้ว ให้คัดลอกเนื้อหาในข้อมูลโค้ดต่อไปนี้ลงในไฟล์ใหม่
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
อัปเดต MainActivity
หากต้องการเชื่อมต่อคอมโพเนนต์ทั้งหมดที่คุณสร้างไว้จนถึงตอนนี้ คุณต้องเพิ่ม 2 บรรทัดในคลาส MainActivity
ก่อนอื่น หากต้องการส่ง InfoWindowAdapter
, MarkerInfoWindowAdapter
ที่กำหนดเองภายในการเรียกเมธอด getMapAsync
ให้เรียกใช้เมธอด setInfoWindowAdapter()
ในออบเจ็กต์ GoogleMap
และสร้างอินสแตนซ์ใหม่ของ MarkerInfoWindowAdapter
- โดยเพิ่มโค้ดต่อไปนี้หลังการเรียกใช้เมธอด
addMarkers()
ภายใน LambdagetMapAsync()
MainActivity.onCreate()
// Set custom info window adapter
googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
สุดท้าย คุณจะต้องตั้งค่าสถานที่แต่ละแห่งเป็นพร็อพเพอร์ตี้แท็กในทุกเครื่องหมายที่เพิ่มลงในแผนที่
- โดยแก้ไข
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
ซึ่งคุณใช้
ตั้งค่าบิตแมปที่กำหนดเองในเครื่องหมาย
เมื่อมีไอคอนจักรยานแบบ Vector Drawable แล้ว ขั้นตอนถัดไปคือการตั้งค่า Drawable นั้นเป็นไอคอนของเครื่องหมายแต่ละรายการบนแผนที่ MarkerOptions
มีเมธอด icon
ซึ่งรับ BitmapDescriptor
ที่คุณใช้เพื่อดำเนินการนี้
ก่อนอื่น คุณต้องแปลง Vector Drawable ที่เพิ่งเพิ่มเป็น 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
- เมื่อใช้พร็อพเพอร์ตี้นี้ ให้เรียกใช้เมธอด
icon
ของMarkerOptions
ในเมธอดaddMarkers()
เพื่อปรับแต่งไอคอนให้เสร็จสมบูรณ์ เมื่อทำเช่นนี้ พร็อพเพอร์ตี้เครื่องหมายควรมีลักษณะดังนี้
MainActivity.addMarkers()
val marker = googleMap.addMarker(
MarkerOptions()
.title(place.name)
.position(place.latLng)
.icon(bicycleIcon)
)
- เรียกใช้แอปเพื่อดูเครื่องหมายที่อัปเดตแล้ว
8. เครื่องหมายคลัสเตอร์
คุณอาจสังเกตเห็นว่าเครื่องหมายที่คุณเพิ่มทับซ้อนกัน ทั้งนี้ขึ้นอยู่กับระดับการซูมแผนที่ เครื่องหมายที่ทับซ้อนกันโต้ตอบได้ยากมากและสร้างสัญญาณรบกวนจำนวนมาก ซึ่งส่งผลต่อความสามารถในการใช้งานของแอป
แนวทางปฏิบัติแนะนำในการปรับปรุงประสบการณ์ของผู้ใช้ในกรณีนี้คือ เมื่อใดก็ตามที่คุณมีชุดข้อมูลขนาดใหญ่ที่จัดกลุ่มอย่างใกล้ชิด ให้ใช้การจัดกลุ่มเครื่องหมาย เมื่อใช้การจัดกลุ่ม ขณะที่คุณซูมเข้าและออกในแผนที่ เครื่องหมายที่อยู่ใกล้กันจะได้รับการจัดกลุ่มเข้าด้วยกันดังนี้
หากต้องการติดตั้งใช้งานฟีเจอร์นี้ คุณต้องได้รับความช่วยเหลือจากไลบรารียูทิลิตี Maps SDK สำหรับ Android
ไลบรารียูทิลิตี Maps SDK สำหรับ Android
ไลบรารียูทิลิตีของ Maps SDK สำหรับ Android สร้างขึ้นเพื่อเป็นวิธีขยายฟังก์ชันการทำงานของ Maps SDK สำหรับ Android โดยมีฟีเจอร์ขั้นสูง เช่น การจัดกลุ่มเครื่องหมาย แผนที่ความร้อน การรองรับ KML และ GeoJson การเข้ารหัสและถอดรหัสเส้นหลายเส้น รวมถึงฟังก์ชันตัวช่วยเล็กๆ น้อยๆ เกี่ยวกับรูปทรงเรขาคณิตทรงกลม
อัปเดต build.gradle
เนื่องจากไลบรารียูทิลิตีได้รับการแพ็กเกจแยกจาก Maps SDK สำหรับ Android คุณจึงต้องเพิ่มทรัพยากร Dependency เพิ่มเติมลงในไฟล์ build.gradle
- โปรดอัปเดตส่วน
dependencies
ของไฟล์app/build.gradle
build.gradle
implementation 'com.google.maps.android:android-maps-utils:1.1.0'
- เมื่อเพิ่มบรรทัดนี้แล้ว คุณต้องซิงค์โปรเจ็กต์เพื่อดึงข้อมูลการอ้างอิงใหม่
ใช้การจัดกลุ่ม
หากต้องการใช้การจัดกลุ่มในแอป ให้ทำตาม 3 ขั้นตอนต่อไปนี้
- ใช้
ClusterItem
อินเทอร์เฟซ - สร้างคลาสย่อยของคลาส
DefaultClusterRenderer
- สร้าง
ClusterManager
แล้วเพิ่มรายการ
ใช้ ClusterItem Interface
ออบเจ็กต์ทั้งหมดที่แสดงเครื่องหมายที่จัดกลุ่มได้บนแผนที่ต้องใช้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
ออบเจ็กต์ที่สร้างขึ้นได้ที่นี่ ในกรณีนี้ ออบเจ็กต์จะตั้งค่าพร็อพเพอร์ตี้แท็กของเครื่องหมาย
สร้าง ClusterManager และเพิ่มรายการ
สุดท้ายนี้ หากต้องการให้การจัดกลุ่มทำงานได้ คุณต้องแก้ไข MainActivity
เพื่อสร้างอินสแตนซ์ ClusterManager
และระบุการอ้างอิงที่จำเป็น ClusterManager
จะจัดการการเพิ่มเครื่องหมาย (ออบเจ็กต์ ClusterItem
) ภายใน ดังนั้นแทนที่จะเพิ่มเครื่องหมายลงในแผนที่โดยตรง ความรับผิดชอบนี้จะมอบหมายให้ ClusterManager
นอกจากนี้ ClusterManager
ยังเรียกใช้ setInfoWindowAdapter()
ภายในด้วย ดังนั้นการตั้งค่าหน้าต่างข้อมูลที่กำหนดเองจะต้องทำในออบเจ็กต์ MarkerManager.Collection
ของ ClusterManger
- หากต้องการเริ่มต้น ให้แก้ไขเนื้อหาของ Lambda ใน
getMapAsync()
การเรียกใช้ในMainActivity.onCreate()
แสดงความคิดเห็นในส่วนที่เรียกใช้addMarkers()
และsetInfoWindowAdapter()
แล้วเรียกใช้เมธอดที่ชื่อaddClusteredMarkers()
แทน ซึ่งคุณจะกำหนดในขั้นตอนถัดไป
MainActivity.onCreate()
mapFragment?.getMapAsync { googleMap ->
//addMarkers(googleMap)
addClusteredMarkers(googleMap)
// Set custom info window adapter.
// googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
}
- ถัดไป ใน
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()
- ไปที่แอปเพื่อดูร้านค้าที่จัดกลุ่มใหม่กันเลย
9. วาดในแผนที่
แม้ว่าคุณจะเคยสำรวจวิธีวาดบนแผนที่ (โดยการเพิ่มเครื่องหมาย) มาแล้ว แต่ Maps SDK สำหรับ Android ก็รองรับวิธีอื่นๆ อีกมากมายที่คุณสามารถใช้วาดเพื่อแสดงข้อมูลที่เป็นประโยชน์บนแผนที่
เช่น หากต้องการแสดงเส้นทางและพื้นที่บนแผนที่ คุณสามารถใช้เส้นหลายเส้นและรูปหลายเหลี่ยมเพื่อแสดงข้อมูลเหล่านี้บนแผนที่ได้ หรือหากต้องการยึดรูปภาพไว้กับพื้นผิว คุณก็ใช้ภาพซ้อนทับบนพื้นได้
ในงานนี้ คุณจะได้เรียนรู้วิธีวาดรูปร่าง โดยเฉพาะวงกลมรอบเครื่องหมายทุกครั้งที่มีการแตะ
เพิ่ม Listener การคลิก
โดยปกติแล้ว วิธีเพิ่ม Listener การคลิกไปยังเครื่องหมายคือการส่ง Listener การคลิกไปยังออบเจ็กต์ GoogleMap
โดยตรงผ่าน setOnMarkerClickListener()
อย่างไรก็ตาม เนื่องจากคุณใช้การจัดกลุ่ม จึงต้องระบุเครื่องมือฟังการคลิกให้กับ ClusterManager
แทน
- ใน
addClusteredMarkers()
วิธีในMainActivity
ให้เพิ่มบรรทัดต่อไปนี้หลังการเรียกใช้ไปยังcluster()
MainActivity.addClusteredMarkers()
// Show polygon
clusterManager.setOnClusterItemClickListener { item ->
addCircle(googleMap, item)
return@setOnClusterItemClickListener false
}
วิธีการนี้จะเพิ่ม Listener และเรียกใช้เมธอด addCircle()
ซึ่งคุณจะกำหนดในขั้นตอนถัดไป สุดท้ายนี้ ระบบจะส่งคืน false
จากเมธอดนี้เพื่อระบุว่าเมธอดนี้ไม่ได้ใช้เหตุการณ์นี้
- จากนั้น คุณต้องกำหนดพร็อพเพอร์ตี้
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 สำหรับการเพิ่มวงกลมนั้นค่อนข้างคล้ายกับการเพิ่มเครื่องหมาย
- ตอนนี้ให้ไปเรียกใช้แอปเพื่อดูการเปลี่ยนแปลง
10. การควบคุมกล้อง
งานสุดท้ายคือการดูการควบคุมกล้องเพื่อให้คุณโฟกัสมุมมองรอบๆ ภูมิภาคหนึ่งๆ ได้
กล้องและมุมมอง
หากคุณสังเกตเห็นเมื่อเรียกใช้แอป กล้องจะแสดงทวีปแอฟริกา และคุณต้องเลื่อนและซูมอย่างละเอียดไปยังซานฟรานซิสโกเพื่อค้นหาเครื่องหมายที่คุณเพิ่ม แม้ว่าจะเป็นวิธีที่สนุกในการสำรวจโลก แต่ก็ไม่เป็นประโยชน์หากคุณต้องการแสดงเครื่องหมายทันที
คุณสามารถตั้งค่าตำแหน่งของกล้องแบบเป็นโปรแกรมเพื่อให้มุมมองอยู่ตรงกลางในตำแหน่งที่ต้องการได้
- ไปที่การเรียก
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()
เพื่อให้ระบบอัปเดตกล้องหลังจากโหลดแผนที่แล้วเท่านั้น ขั้นตอนนี้จำเป็นเนื่องจากต้องคำนวณพร็อพเพอร์ตี้ของแผนที่ เช่น ขนาด ก่อนที่จะทำการเรียกอัปเดตกล้อง
ใน Lambda จะมีการสร้างออบเจ็กต์ LatLngBounds
ใหม่ ซึ่งกำหนดภูมิภาคสี่เหลี่ยมผืนผ้าบนแผนที่ โดยจะสร้างขึ้นทีละส่วนด้วยการรวมค่า LatLng
ของสถานที่ทั้งหมดไว้ในนั้นเพื่อให้แน่ใจว่าสถานที่ทั้งหมดอยู่ภายในขอบเขต เมื่อสร้างออบเจ็กต์นี้แล้ว ระบบจะเรียกใช้เมธอด moveCamera()
ใน GoogleMap
และส่ง CameraUpdate
ไปให้ผ่าน CameraUpdateFactory.newLatLngBounds(bounds.build(), 20)
- เรียกใช้แอปและสังเกตว่าตอนนี้กล้องเริ่มต้นใช้งานในซานฟรานซิสโกแล้ว
การฟังการเปลี่ยนแปลงของกล้อง
นอกจากจะปรับเปลี่ยนตำแหน่งกล้องแล้ว คุณยังฟังข้อมูลอัปเดตกล้องขณะที่ผู้ใช้เคลื่อนที่ไปรอบๆ แผนที่ได้ด้วย ซึ่งอาจมีประโยชน์หากคุณต้องการแก้ไข UI ขณะที่กล้องเคลื่อนที่
คุณแก้ไขโค้ดเพื่อทำให้เครื่องหมายโปร่งแสงทุกครั้งที่ย้ายกล้องได้เพื่อความสนุก
- ใน
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
เพื่อให้เมื่อใดก็ตามที่กล้องเริ่มเคลื่อนไหว ระบบจะแก้ไขค่าอัลฟ่าของเครื่องหมายทั้งหมด (ทั้งคลัสเตอร์และเครื่องหมาย) เป็น 0.3f
เพื่อให้เครื่องหมายปรากฏเป็นแบบโปร่งแสง
- สุดท้ายนี้ หากต้องการแก้ไขเครื่องหมายกึ่งโปร่งแสงให้กลับมาเป็นทึบแสงเมื่อกล้องหยุด ให้แก้ไขเนื้อหาของ
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()
}
- เรียกใช้แอปเพื่อดูผลลัพธ์ได้เลย
11. Maps KTX
สำหรับแอป Kotlin ที่ใช้ Google Maps Platform Android SDK อย่างน้อย 1 รายการ ไลบรารีส่วนขยาย Kotlin หรือ KTX จะพร้อมใช้งานเพื่อให้คุณใช้ประโยชน์จากฟีเจอร์ภาษา Kotlin เช่น Coroutine, พร็อพเพอร์ตี้/ฟังก์ชันส่วนขยาย และอื่นๆ ได้ Google Maps SDK แต่ละรายการมีไลบรารี KTX ที่เกี่ยวข้องดังที่แสดงด้านล่าง
ในงานนี้ คุณจะได้ใช้ไลบรารี Maps KTX และ Maps Utils KTX กับแอป และปรับโครงสร้างการใช้งานของงานก่อนหน้าเพื่อให้ใช้ฟีเจอร์ภาษาเฉพาะของ Kotlin ในแอปได้
- รวมทรัพยากร Dependency KTX ในไฟล์ build.gradle ระดับแอป
เนื่องจากแอปใช้ทั้ง Maps SDK สำหรับ Android และไลบรารียูทิลิตี Maps SDK สำหรับ Android คุณจึงต้องรวมไลบรารี KTX ที่เกี่ยวข้องสำหรับไลบรารีเหล่านี้ นอกจากนี้ คุณยังจะได้ใช้ฟีเจอร์ที่อยู่ในไลบรารี KTX ของ AndroidX Lifecycle ในงานนี้ด้วย ดังนั้นให้รวมทรัพยากร 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'
}
- ใช้ฟังก์ชันส่วนขยาย GoogleMap.addMarker() และ GoogleMap.addCircle()
ไลบรารี Maps KTX มี API ทางเลือกในรูปแบบ DSL สำหรับ GoogleMap.addMarker(MarkerOptions)
และ GoogleMap.addCircle(CircleOptions)
ที่ใช้ในขั้นตอนก่อนหน้า หากต้องการใช้ API ที่กล่าวถึงข้างต้น คุณต้องสร้างคลาสที่มีตัวเลือกสำหรับเครื่องหมายหรือวงกลม ในขณะที่ตัวเลือก KTX ช่วยให้คุณตั้งค่าเครื่องหมายหรือวงกลมใน Lambda ที่คุณระบุได้
หากต้องการใช้ API เหล่านี้ ให้อัปเดตเมธอด MainActivity.addMarkers(GoogleMap)
และ MainActivity.addCircle(GoogleMap)
ดังนี้
MainActivity.addMarkers(GoogleMap)
/**
* 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(GoogleMap)
/**
* 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
- ใช้ฟังก์ชันส่วนขยายที่ระงับ SupportMapFragment.awaitMap() และ GoogleMap.awaitMapLoad()
นอกจากนี้ ไลบรารี Maps KTX ยังมีส่วนขยายฟังก์ชันที่ระงับการทำงานเพื่อใช้ภายในโครูทีนด้วย โดยเฉพาะอย่างยิ่ง มีฟังก์ชันการระงับทางเลือกสำหรับ SupportMapFragment.getMapAsync(OnMapReadyCallback)
และ GoogleMap.setOnMapLoadedCallback(OnMapLoadedCallback)
การใช้ API ทางเลือกเหล่านี้จะช่วยให้คุณไม่ต้องส่งการเรียกกลับ และช่วยให้คุณรับการตอบกลับของเมธอดเหล่านี้ในลักษณะอนุกรมและแบบซิงโครนัสได้แทน
เนื่องจากเมธอดเหล่านี้เป็นฟังก์ชันที่ระงับ การใช้งานจึงต้องเกิดขึ้นภายในโครูทีน ไลบรารี Lifecycle Runtime KTX มีส่วนขยายเพื่อจัดเตรียมขอบเขตของโครูทีนที่รับรู้ถึงวงจรของกิจกรรม เพื่อให้โครูทีนทำงานและหยุดทำงานในเหตุการณ์วงจรของกิจกรรมที่เหมาะสม
เมื่อรวมแนวคิดเหล่านี้แล้ว ให้อัปเดตเมธอด MainActivity.onCreate(Bundle)
ดังนี้
MainActivity.onCreate(Bundle)
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()
ตามลำดับ การปรับโครงสร้างโค้ดโดยใช้ฟังก์ชันระงับเหล่านี้ช่วยให้คุณเขียนโค้ดที่เทียบเท่าซึ่งอิงตามการเรียกกลับในลักษณะลำดับได้
- ตอนนี้คุณก็สร้างแอปใหม่โดยใช้การเปลี่ยนแปลงที่รีแฟกเตอร์แล้วได้เลย
12. ขอแสดงความยินดี
ยินดีด้วย คุณได้เรียนรู้เนื้อหามากมาย และเราหวังว่าคุณจะเข้าใจฟีเจอร์หลักที่อยู่ใน Maps SDK สำหรับ Android ได้ดียิ่งขึ้น
ดูข้อมูลเพิ่มเติม
- Places SDK สำหรับ Android - สำรวจชุดข้อมูลสถานที่ที่ครอบคลุมเพื่อค้นหาธุรกิจรอบตัวคุณ
- android-maps-ktx - ไลบรารีโอเพนซอร์สที่ช่วยให้คุณผสานรวมกับ Maps SDK สำหรับ Android และ Maps SDK สำหรับ Android Utility Library ในลักษณะที่เป็นมิตรกับ Kotlin
- android-place-ktx - ไลบรารีโอเพนซอร์สที่ช่วยให้คุณผสานรวมกับ Places SDK สำหรับ Android ในลักษณะที่เป็นมิตรกับ Kotlin
- android-samples - โค้ดตัวอย่างใน GitHub ที่แสดงฟีเจอร์ทั้งหมดที่กล่าวถึงในโค้ดแล็บนี้และอื่นๆ
- Codelab ของ Kotlin เพิ่มเติมสำหรับการสร้างแอป Android ด้วย Google Maps Platform