Android ขั้นสูงใน Kotlin 04.1: Google Maps สำหรับ Android

1. ก่อนเริ่มต้น

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

ในบทเรียนนี้ คุณจะได้สร้างแอป Google Maps ชื่อ Wander ซึ่งจะแสดงแผนที่ที่ปรับแต่งแล้วและแสดงตำแหน่งของผู้ใช้

ข้อกำหนดเบื้องต้น

ความรู้เกี่ยวกับสิ่งต่อไปนี้

  • วิธีสร้างแอป Android พื้นฐานและเรียกใช้โดยใช้ Android Studio
  • วิธีสร้างและจัดการทรัพยากร เช่น สตริง
  • วิธีปรับโครงสร้างโค้ดและเปลี่ยนชื่อตัวแปรโดยใช้ Android Studio
  • วิธีใช้แผนที่ Google ในฐานะผู้ใช้
  • วิธีตั้งค่าสิทธิ์รันไทม์

สิ่งที่คุณจะได้เรียนรู้

  • วิธีรับคีย์ API จาก Google API Console และลงทะเบียนคีย์กับแอป
  • วิธีผสานรวม Google Maps ในแอป
  • วิธีแสดงแผนที่ประเภทต่างๆ
  • วิธีจัดรูปแบบ Google Map
  • วิธีเพิ่มเครื่องหมายลงในแผนที่
  • วิธีเปิดใช้เพื่อให้ผู้ใช้ปักหมุดที่จุดที่น่าสนใจ (POI)
  • วิธีเปิดใช้การติดตามตำแหน่ง
  • วิธีสร้างแอป Wander ซึ่งมี Google Maps แบบฝัง
  • วิธีสร้างฟีเจอร์ที่กำหนดเองสำหรับแอป เช่น เครื่องหมายและการจัดรูปแบบ
  • วิธีเปิดใช้การติดตามตำแหน่งในแอป

2. ภาพรวมของแอป

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

5b12eda7f467bc2f.png

3. งาน: ตั้งค่าโปรเจ็กต์และรับคีย์ API

Maps SDK สำหรับ Android ต้องใช้คีย์ API หากต้องการรับคีย์ API ให้ลงทะเบียนโปรเจ็กต์ในหน้า API และบริการ คีย์ API จะเชื่อมโยงกับใบรับรองดิจิทัลที่ลิงก์แอปกับผู้เขียน ดูข้อมูลเพิ่มเติมเกี่ยวกับการใช้ใบรับรองดิจิทัลและการลงนามแอปได้ที่ลงนามแอป

ในโค้ดแล็บนี้ คุณจะใช้คีย์ API สำหรับใบรับรองการแก้ไขข้อบกพร่อง ใบรับรองการแก้ไขข้อบกพร่องไม่ปลอดภัยตามการออกแบบ ดังที่อธิบายไว้ในลงนามในบิลด์การแก้ไขข้อบกพร่อง แอป Android ที่เผยแพร่แล้วซึ่งใช้ Maps SDK สำหรับ Android ต้องใช้คีย์ API ที่ 2 ซึ่งเป็นคีย์สำหรับใบรับรองการเผยแพร่ ดูข้อมูลเพิ่มเติมเกี่ยวกับการขอรับใบรับรองรุ่นได้ที่รับคีย์ API

Android Studio มีเทมเพลตกิจกรรม Google Maps ซึ่งสร้างโค้ดเทมเพลตที่เป็นประโยชน์ โค้ดเทมเพลตมีไฟล์ google_maps_api.xml ซึ่งมีลิงก์ที่ช่วยให้รับคีย์ API ได้ง่ายขึ้น

ขั้นตอนที่ 1: สร้างโปรเจ็กต์ Wander ด้วยเทมเพลตแผนที่

  1. สร้างโปรเจ็กต์ Android Studio ใหม่
  2. เลือกเทมเพลตกิจกรรมใน Google Maps

d6b874bb19ea68cd.png

  1. ตั้งชื่อโปรเจ็กต์ Wander
  2. ตั้งค่าระดับ API ขั้นต่ำเป็น API 19 ตรวจสอบว่าภาษาเป็น Kotlin
  3. คลิกเสร็จสิ้น
  4. เมื่อสร้างแอปเสร็จแล้ว ให้ดูโปรเจ็กต์และไฟล์ที่เกี่ยวข้องกับ Maps ต่อไปนี้ที่ Android Studio สร้างให้คุณ

google_maps_api.xml - คุณใช้ไฟล์การกำหนดค่านี้เพื่อจัดเก็บคีย์ API เทมเพลตจะสร้างไฟล์ google_maps_api.xml 2 ไฟล์ ได้แก่ ไฟล์สําหรับการแก้ไขข้อบกพร่องและไฟล์สําหรับการเผยแพร่ ไฟล์สำหรับคีย์ API ของใบรับรองการแก้ไขข้อบกพร่องจะอยู่ใน src/debug/res/values ไฟล์สำหรับคีย์ API สำหรับใบรับรองการเผยแพร่อยู่ใน src/release/res/values ใน Codelab นี้ คุณจะใช้เฉพาะใบรับรองการแก้ไขข้อบกพร่อง

activity_maps.xml - ไฟล์เลย์เอาต์นี้มี Fragment เดียวที่ครอบคลุมทั้งหน้าจอ คลาส SupportMapFragment เป็นคลาสย่อยของคลาส Fragment SupportMapFragment เป็นวิธีที่ง่ายที่สุดในการวางแผนที่ในแอป ซึ่งเป็น Wrapper รอบมุมมองของแผนที่เพื่อจัดการความต้องการด้านวงจรที่จำเป็นโดยอัตโนมัติ

คุณใส่ SupportMapFragment ในไฟล์เลย์เอาต์ได้โดยใช้แท็ก <fragment> ใน ViewGroup ใดก็ได้ พร้อมด้วยแอตทริบิวต์ name เพิ่มเติม

android:name="com.google.android.gms.maps.SupportMapFragment"

MapsActivity.java - ไฟล์ MapsActivity.kt จะสร้างอินสแตนซ์ SupportMapFragment ในเมธอด onCreate() และใช้ getMapAsync() ของคลาสเพื่อเริ่มต้นระบบแผนที่และมุมมองโดยอัตโนมัติ กิจกรรมที่มี SupportMapFragment ต้องใช้การเชื่อมต่อ OnMapReadyCallback และเมธอด onMapReady() ของการเชื่อมต่อดังกล่าว ระบบจะเรียกใช้เมธอด onMapReady() เมื่อโหลดแผนที่

ขั้นตอนที่ 2: รับคีย์ API

  1. เปิดไฟล์ google_maps_api.xml เวอร์ชันแก้ไขข้อบกพร่อง
  2. ในไฟล์ ให้มองหาความคิดเห็นที่มี URL ยาว พารามิเตอร์ของ URL มีข้อมูลเฉพาะเกี่ยวกับแอปของคุณ
  3. คัดลอกและวาง URL ลงในเบราว์เซอร์
  4. ทำตามข้อความแจ้งเพื่อสร้างโปรเจ็กต์ในหน้า API และบริการ เนื่องจากพารามิเตอร์ใน URL ที่ระบุ หน้าเว็บจึงทราบว่าต้องเปิดใช้ Maps SDK สำหรับ Android โดยอัตโนมัติ
  5. คลิกสร้างคีย์ API
  6. ในหน้าถัดไป ให้ไปที่ส่วนคีย์ API แล้วคลิกคีย์ที่คุณเพิ่งสร้าง
  7. คลิกจำกัดคีย์ แล้วเลือก Maps SDK สำหรับ Android เพื่อจำกัดการใช้คีย์ไว้เฉพาะแอป Android
  8. คัดลอกคีย์ API ที่สร้างขึ้น โดยจะขึ้นต้นด้วย "AIza"
  9. ในgoogle_maps_api.xmlไฟล์ ให้วางคีย์ลงในสตริง google_maps_key ที่มีข้อความ YOUR_KEY_HERE
  10. เรียกใช้แอป คุณควรเห็นแผนที่ฝังในกิจกรรมโดยมีเครื่องหมายที่ตั้งอยู่ในซิดนีย์ ออสเตรเลีย (เครื่องหมายซิดนีย์เป็นส่วนหนึ่งของเทมเพลตและคุณจะเปลี่ยนได้ในภายหลัง)

34dc9dd877c90996.png

ขั้นตอนที่ 3: เปลี่ยนชื่อ mMap

MapsActivity มี lateinit var แบบส่วนตัวชื่อ mMap ซึ่งเป็นประเภท GoogleMap หากต้องการทำตามรูปแบบการตั้งชื่อของ Kotlin ให้เปลี่ยนชื่อ mMap เป็น map

  1. ใน MapsActivity ให้คลิกขวาที่ mMap แล้วคลิกปรับโครงสร้าง > เปลี่ยนชื่อ...

e713ccb3384450c6.png

  1. เปลี่ยนชื่อตัวแปรเป็น map

โปรดสังเกตว่าการอ้างอิงทั้งหมดถึง mMap ในฟังก์ชัน onMapReady() จะเปลี่ยนเป็น map ด้วย

4. งาน: เพิ่มประเภทแผนที่

Google Maps มีแผนที่หลายประเภท ได้แก่ ปกติ ไฮบริด ดาวเทียม ภูมิประเทศ และ "ไม่มี" (สำหรับไม่มีแผนที่เลย)

แผนที่ปกติ

แผนที่ดาวเทียม

แผนที่ไฮบริด

แผนที่ภูมิประเทศ

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

ในงานนี้ คุณจะต้องทำสิ่งต่อไปนี้

  1. เพิ่มแถบแอปที่มีเมนูตัวเลือกซึ่งช่วยให้ผู้ใช้เปลี่ยนประเภทแผนที่ได้
  2. ย้ายตำแหน่งเริ่มต้นของแผนที่ไปยังตำแหน่งบ้านของคุณ
  3. เพิ่มการรองรับเครื่องหมาย ซึ่งระบุตำแหน่งเดียวบนแผนที่และอาจมีป้ายกำกับ

เพิ่มเมนูสำหรับประเภทแผนที่

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

  1. หากต้องการสร้างไฟล์ XML ของเมนูใหม่ ให้คลิกขวาที่ไดเรกทอรี res แล้วเลือกใหม่ > ไฟล์ทรัพยากร Android
  2. ในกล่องโต้ตอบ ให้ตั้งชื่อไฟล์ map_options
  3. เลือก Menu เป็นประเภททรัพยากร
  4. คลิกตกลง
  5. ในแท็บโค้ด ให้แทนที่โค้ดในไฟล์ใหม่ด้วยโค้ดต่อไปนี้เพื่อสร้างตัวเลือกเมนูแผนที่ ระบบจะละเว้นประเภทแผนที่ "ไม่มี" เนื่องจาก "ไม่มี" จะส่งผลให้ไม่มีแผนที่เลย ขั้นตอนนี้จะทำให้เกิดข้อผิดพลาด แต่คุณจะแก้ไขได้ในขั้นตอนถัดไป
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto">
   <item
       android:id="@+id/normal_map"
       android:title="@string/normal_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/hybrid_map"
       android:title="@string/hybrid_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/satellite_map"
       android:title="@string/satellite_map"
       app:showAsAction="never" />
   <item
       android:id="@+id/terrain_map"
       android:title="@string/terrain_map"
       app:showAsAction="never" />
</menu>
  1. ใน strings.xml ให้เพิ่มแหล่งข้อมูลสำหรับแอตทริบิวต์ title เพื่อแก้ไขข้อผิดพลาด
<resources>
   ...
   <string name="normal_map">Normal Map</string>
   <string name="hybrid_map">Hybrid Map</string>
   <string name="satellite_map">Satellite Map</string>
   <string name="terrain_map">Terrain Map</string>
   <string name="lat_long_snippet">Lat: %1$.5f, Long: %2$.5f</string>
   <string name="dropped_pin">Dropped Pin</string>
   <string name="poi">poi</string>
</resources>
  1. ใน MapsActivity ให้ลบล้างเมธอด onCreateOptionsMenu() และขยายเมนูจากไฟล์ทรัพยากร map_options
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
   val inflater = menuInflater
   inflater.inflate(R.menu.map_options, menu)
   return true
}
  1. ใน MapsActivity.kt ให้ลบล้างเมธอด onOptionsItemSelected() เปลี่ยนประเภทแผนที่โดยใช้ค่าคงที่ของประเภทแผนที่เพื่อให้สอดคล้องกับการเลือกของผู้ใช้
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
   // Change the map type based on the user's selection.
   R.id.normal_map -> {
       map.mapType = GoogleMap.MAP_TYPE_NORMAL
       true
   }
   R.id.hybrid_map -> {
       map.mapType = GoogleMap.MAP_TYPE_HYBRID
       true
   }
   R.id.satellite_map -> {
       map.mapType = GoogleMap.MAP_TYPE_SATELLITE
       true
   }
   R.id.terrain_map -> {
       map.mapType = GoogleMap.MAP_TYPE_TERRAIN
       true
   }
   else -> super.onOptionsItemSelected(item)
}
  1. เรียกใช้แอป
  2. คลิก 428da163b831115b.png เพื่อเปลี่ยนประเภทแผนที่ สังเกตว่าลักษณะที่ปรากฏของแผนที่เปลี่ยนแปลงไปอย่างไรในโหมดต่างๆ

6fa42970d87f5dc7.png

5. งาน: เพิ่มเครื่องหมาย

โดยค่าเริ่มต้น onMapReady() การเรียกกลับจะมีโค้ดที่วางเครื่องหมายในซิดนีย์ ประเทศออสเตรเลีย ซึ่งเป็นที่ที่สร้าง Google Maps การเรียกกลับเริ่มต้นจะทำให้แผนที่เคลื่อนไหวเพื่อเลื่อนไปยังซิดนีย์ด้วย

ในงานนี้ คุณจะทำให้กล้องของแผนที่เคลื่อนไปยังบ้าน ซูมไปยังระดับที่คุณระบุ และวางเครื่องหมายไว้ที่นั่น

ขั้นตอนที่ 1: ซูมไปที่บ้านและเพิ่มเครื่องหมาย

  1. ในไฟล์ MapsActivity.kt ให้ค้นหาวิธี onMapReady() นำโค้ดที่วางเครื่องหมายในซิดนีย์และย้ายกล้องออก ตอนนี้เมธอดของคุณควรมีลักษณะดังนี้
override fun onMapReady(googleMap: GoogleMap) {
   map = googleMap

}
  1. ค้นหาละติจูดและลองจิจูดของบ้านโดยทำตามวิธีการเหล่านี้
  2. สร้างค่าสำหรับละติจูดและค่าสำหรับลองจิจูด แล้วป้อนค่าลอยตัวของค่าเหล่านั้น
val latitude = 37.422160
val longitude = -122.084270
  1. สร้างออบเจ็กต์ LatLng ใหม่ชื่อ homeLatLng ในออบเจ็กต์ homeLatLng ให้ส่งค่าที่คุณเพิ่งสร้าง
val homeLatLng = LatLng(latitude, longitude)
  1. สร้าง val สำหรับระดับการซูมที่คุณต้องการในแผนที่ ใช้ระดับการซูม 15f
val zoomLevel = 15f

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

  • 1: โลก
  • 5: ผืนดินขนาดใหญ่/ทวีป
  • 10: เมือง
  • 15: ถนน
  • 20: อาคาร
  1. ย้ายกล้องไปที่ homeLatLng โดยเรียกฟังก์ชัน moveCamera() ในออบเจ็กต์ map และส่งออบเจ็กต์ CameraUpdate โดยใช้ CameraUpdateFactory.newLatLngZoom() ส่งออบเจ็กต์ homeLatLng และ zoomLevel
map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
  1. เพิ่มเครื่องหมายลงในแผนที่ที่ homeLatLng
map.addMarker(MarkerOptions().position(homeLatLng))

วิธีสุดท้ายควรมีลักษณะดังนี้

override fun onMapReady(googleMap: GoogleMap) {
   map = googleMap

   //These coordinates represent the latitude and longitude of the Googleplex.
   val latitude = 37.422160
   val longitude = -122.084270
   val zoomLevel = 15f

   val homeLatLng = LatLng(latitude, longitude)
   map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
   map.addMarker(MarkerOptions().position(homeLatLng))
}
  1. เรียกใช้แอป แผนที่ควรเลื่อนไปยังบ้าน ซูมไปยังระดับที่ต้องการ และวางเครื่องหมายบนบ้าน

fc939024778ee76.png

ขั้นตอนที่ 2: อนุญาตให้ผู้ใช้เพิ่มเครื่องหมายโดยใช้การคลิกแบบยาว

ในขั้นตอนนี้ คุณจะเพิ่มเครื่องหมายเมื่อผู้ใช้แตะตำแหน่งบนแผนที่ค้างไว้

  1. สร้าง Stub ของเมธอดใน MapsActivity ชื่อ setMapLongClick() ซึ่งรับ GoogleMap เป็นอาร์กิวเมนต์
  2. แนบเครื่องฟังsetOnMapLongClickListenerกับออบเจ็กต์แผนที่
private fun setMapLongClick(map:GoogleMap) {
   map.setOnMapLongClickListener { }
}
  1. ใน setOnMapLongClickListener() ให้เรียกใช้เมธอด addMarker() ส่งออบเจ็กต์ MarkerOptions ใหม่โดยตั้งค่าตำแหน่งเป็น LatLng ที่ส่งเข้ามา
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. ที่ส่วนท้ายของเมธอด onMapReady() ให้เรียกใช้ setMapLongClick() ด้วย map
override fun onMapReady(googleMap: GoogleMap) {
   ...
  
   setMapLongClick(map)
}
  1. เรียกใช้แอป
  2. แตะแผนที่ค้างไว้เพื่อวางเครื่องหมายที่ตำแหน่ง
  3. แตะเครื่องหมายเพื่อจัดกึ่งกลางบนหน้าจอ

4ff8d1c1db3bca9e.png

ขั้นตอนที่ 3: เพิ่มหน้าต่างข้อมูลสำหรับเครื่องหมาย

ในขั้นตอนนี้ คุณจะเพิ่ม InfoWindow ที่แสดงพิกัดของเครื่องหมายเมื่อมีการแตะเครื่องหมาย

  1. ใน setMapLongClick()setOnMapLongClickListener() ให้สร้าง val สำหรับ snippet ข้อมูลโค้ดคือข้อความเพิ่มเติมที่แสดงหลังชื่อ ข้อมูลโค้ดจะแสดงละติจูดและลองจิจูดของเครื่องหมาย
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       // A snippet is additional text that's displayed after the title.
       val snippet = String.format(
           Locale.getDefault(),
           "Lat: %1$.5f, Long: %2$.5f",
           latLng.latitude,
           latLng.longitude
       )
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. ใน addMarker() ให้ตั้งค่า title ของเครื่องหมายเป็นหมุดที่วางโดยใช้แหล่งข้อมูลสตริง R.string.dropped_pin
  2. ตั้งค่า snippet ของเครื่องหมายเป็น snippet

ฟังก์ชันที่เสร็จสมบูรณ์แล้วจะมีลักษณะดังนี้

private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       // A Snippet is Additional text that's displayed below the title.
       val snippet = String.format(
           Locale.getDefault(),
           "Lat: %1$.5f, Long: %2$.5f",
           latLng.latitude,
           latLng.longitude
       )
       map.addMarker(
           MarkerOptions()
               .position(latLng)
               .title(getString(R.string.dropped_pin))
               .snippet(snippet)
              
       )
   }
}
  1. เรียกใช้แอป
  2. แตะแผนที่ค้างไว้เพื่อวางเครื่องหมายระบุตำแหน่ง
  3. แตะเครื่องหมายเพื่อแสดงหน้าต่างข้อมูล

63f210e6e47dfa29.png

ขั้นตอนที่ 4: เพิ่มเครื่องมือฟัง POI

โดยค่าเริ่มต้น จุดที่น่าสนใจ (POI) จะปรากฏบนแผนที่พร้อมกับไอคอนที่เกี่ยวข้อง POI ได้แก่ สวนสาธารณะ โรงเรียน อาคารรัฐบาล และอื่นๆ เมื่อตั้งค่าประเภทแผนที่เป็น normal จุดที่น่าสนใจของธุรกิจจะปรากฏบนแผนที่ด้วย POI ของธุรกิจแสดงถึงธุรกิจต่างๆ เช่น ร้านค้า ร้านอาหาร และโรงแรม

ในขั้นตอนนี้ คุณจะเพิ่ม GoogleMap.OnPoiClickListener ลงในแผนที่ เครื่องมือฟังการคลิกนี้จะวางเครื่องหมายบนแผนที่ทันทีเมื่อผู้ใช้คลิกจุดที่น่าสนใจ ตัวตรวจจับการคลิกจะแสดงหน้าต่างข้อมูลที่มีชื่อจุดที่น่าสนใจด้วย

  1. สร้าง Stub ของเมธอดใน MapsActivity ชื่อ setPoiClick() ซึ่งรับ GoogleMap เป็นอาร์กิวเมนต์
  2. ในเมธอด setPoiClick() ให้ตั้งค่า OnPoiClickListener ใน GoogleMap ที่ส่งเข้ามา
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->

   }
}
  1. ใน setOnPoiClickListener() ให้สร้าง val poiMarker สำหรับเครื่องหมาย
  2. ตั้งค่าเป็นเครื่องหมายโดยใช้ map.addMarker() กับ MarkerOptions โดยตั้งค่า title เป็นชื่อของจุดที่น่าสนใจ
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
   }
}
  1. ในฟังก์ชัน setOnPoiClickListener() ให้เรียกใช้ showInfoWindow() ใน poiMarker เพื่อแสดงหน้าต่างข้อมูลทันที
poiMarker.showInfoWindow()

โค้ดสุดท้ายสำหรับฟังก์ชัน setPoiClick() ควรมีลักษณะดังนี้

private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
       poiMarker.showInfoWindow()
   }
}
  1. เมื่อสิ้นสุด onMapReady() ให้โทรหา setPoiClick() และส่ง map
override fun onMapReady(googleMap: GoogleMap) {
   ...

   setPoiClick(map)
}
  1. เรียกใช้แอปและค้นหาสถานที่น่าสนใจ เช่น สวนสาธารณะหรือร้านกาแฟ
  2. แตะจุดที่น่าสนใจเพื่อวางเครื่องหมายและแสดงชื่อของจุดที่น่าสนใจในหน้าต่างข้อมูล

f4b0972c75d5fa5f.png

6. งาน: จัดรูปแบบแผนที่

คุณปรับแต่ง Google Maps ได้หลายวิธีเพื่อให้แผนที่มีรูปลักษณ์และให้ความรู้สึกที่ไม่ซ้ำใคร

คุณปรับแต่งออบเจ็กต์ MapFragment ได้โดยใช้แอตทริบิวต์ XML ที่มีอยู่ เช่นเดียวกับการปรับแต่ง Fragment อื่นๆ อย่างไรก็ตาม ในขั้นตอนนี้ คุณจะปรับแต่งรูปลักษณ์ของเนื้อหาของ MapFragment โดยใช้วิธีการในออบเจ็กต์ GoogleMap

หากต้องการสร้างสไตล์ที่กำหนดเองสำหรับแผนที่ ให้สร้างไฟล์ JSON ที่ระบุวิธีแสดงองค์ประกอบในแผนที่ คุณไม่จำเป็นต้องสร้างไฟล์ JSON นี้ด้วยตนเอง Google มีวิซาร์ดการจัดรูปแบบ Maps Platform ซึ่งจะสร้าง JSON ให้คุณหลังจากจัดรูปแบบแผนที่ด้วยภาพ ในงานนี้ คุณจะจัดรูปแบบแผนที่ด้วยธีมย้อนยุค ซึ่งหมายความว่าแผนที่จะใช้สีแบบวินเทจและคุณจะเพิ่มถนนที่มีสี

ขั้นตอนที่ 1: สร้างสไตล์สำหรับแผนที่

  1. ไปที่ https://mapstyle.withgoogle.com/ ในเบราว์เซอร์
  2. เลือกสร้างสไตล์
  3. เลือก Retro

208b3d3aeab0d9b6.png

  1. คลิกตัวเลือกเพิ่มเติม

4a35faaf9535ee82.png

  1. เลือกถนน > เติม
  2. เปลี่ยนสีถนนเป็นสีใดก็ได้ที่คุณเลือก (เช่น สีชมพู)

92c3293749293a4c.png

  1. คลิกเสร็จสิ้น

f1bfe8585eb69480.png

  1. คัดลอกโค้ด JSON จากกล่องโต้ตอบที่ได้ และหากต้องการ ให้เก็บโค้ดไว้ในโน้ตข้อความธรรมดาเพื่อใช้ในขั้นตอนถัดไป

3c32168b299d6420.png

ขั้นตอนที่ 2: เพิ่มสไตล์ลงในแผนที่

  1. ใน Android Studio ให้สร้างไดเรกทอรีทรัพยากรในไดเรกทอรี res แล้วตั้งชื่อเป็น raw คุณใช้ทรัพยากรไดเรกทอรี raw เช่น โค้ด JSON
  2. สร้างไฟล์ใน res/raw ชื่อ map_style.json
  3. วางโค้ด JSON ที่ซ่อนไว้ลงในไฟล์ทรัพยากรใหม่
  4. ใน MapsActivity ให้สร้างตัวแปรคลาส TAG เหนือเมธอด onCreate() ซึ่งใช้เพื่อวัตถุประสงค์ในการบันทึก
private val TAG = MapsActivity::class.java.simpleName
  1. นอกจากนี้ ใน MapsActivity ให้สร้างฟังก์ชัน setMapStyle() ที่รับ GoogleMap
  2. ใน setMapStyle() ให้เพิ่มบล็อก try{}
  3. ในtry{} ให้สร้าง val success เพื่อให้การจัดรูปแบบสำเร็จ (คุณเพิ่มบล็อก catch ต่อไปนี้)
  4. ในtry{} ให้ตั้งค่ารูปแบบ JSON เป็นแผนที่ เรียกใช้ setMapStyle() ในออบเจ็กต์ GoogleMap ส่งออบเจ็กต์ MapStyleOptions ซึ่งจะโหลดไฟล์ JSON
  5. มอบหมายผลลัพธ์ให้ success setMapStyle() เมธอดจะแสดงผลบูลีนที่ระบุสถานะความสำเร็จของการแยกวิเคราะห์ไฟล์การจัดรูปแบบและการตั้งค่ารูปแบบ
private fun setMapStyle(map: GoogleMap) {
   try {
       // Customize the styling of the base map using a JSON object defined
       // in a raw resource file.
       val success = map.setMapStyle(
           MapStyleOptions.loadRawResourceStyle(
               this,
               R.raw.map_style
           )
       )
   }
}
  1. เพิ่มคำสั่ง if สำหรับ success ที่เป็นเท็จ หากจัดรูปแบบไม่สำเร็จ ให้พิมพ์บันทึกว่าการแยกวิเคราะห์ล้มเหลว
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
       if (!success) {
           Log.e(TAG, "Style parsing failed.")
       }
   }
}
  1. เพิ่มcatch{}บล็อกเพื่อจัดการกรณีที่ไม่มีไฟล์สไตล์ ในบล็อก catch หากโหลดไฟล์ไม่ได้ ให้ส่ง Resources.NotFoundException
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
   } catch (e: Resources.NotFoundException) {
       Log.e(TAG, "Can't find style. Error: ", e)
   }
}

เมธอดที่เสร็จสมบูรณ์แล้วควรมีลักษณะเหมือนข้อมูลโค้ดต่อไปนี้

private fun setMapStyle(map: GoogleMap) {
   try {
       // Customize the styling of the base map using a JSON object defined
       // in a raw resource file.
       val success = map.setMapStyle(
           MapStyleOptions.loadRawResourceStyle(
               this,
               R.raw.map_style
           )
       )

       if (!success) {
           Log.e(TAG, "Style parsing failed.")
       }
   } catch (e: Resources.NotFoundException) {
       Log.e(TAG, "Can't find style. Error: ", e)
   }
}
  1. สุดท้าย ให้เรียกใช้เมธอด setMapStyle() ในเมธอด onMapReady() โดยส่งออบเจ็กต์ GoogleMap
override fun onMapReady(googleMap: GoogleMap) {
   ...
   setMapStyle(map)
}
  1. เรียกใช้แอป
  2. ตั้งค่าแผนที่เป็นโหมด normal แล้วคุณจะเห็นการจัดรูปแบบใหม่พร้อมธีมย้อนยุคและถนนในสีที่คุณเลือก

b59d6cb81f02a14f.png

ขั้นตอนที่ 3: จัดรูปแบบเครื่องหมาย

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

  1. ในเมธอด onMapLongClick() ให้เพิ่มบรรทัดโค้ดต่อไปนี้ลงใน MarkerOptions() ของตัวสร้างเพื่อใช้เครื่องหมายเริ่มต้น แต่เปลี่ยนสีเป็นสีน้ำเงิน
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))

ตอนนี้ onMapLongClickListener() มีลักษณะดังนี้

map.setOnMapLongClickListener { latLng ->
   // A snippet is additional text that's displayed after the title.
   val snippet = String.format(
       Locale.getDefault(),
       "Lat: %1$.5f, Long: %2$.5f",
       latLng.latitude,
       latLng.longitude
   )
   map.addMarker(
       MarkerOptions()
           .position(latLng)
           .title(getString(R.string.dropped_pin))
           .snippet(snippet)
         .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))
   )
}
  1. เรียกใช้แอป ตอนนี้เครื่องหมายที่ปรากฏหลังจากที่คุณคลิกค้างไว้จะเป็นสีน้ำเงิน โปรดทราบว่าเครื่องหมาย POI ยังคงเป็นสีแดงเนื่องจากคุณไม่ได้เพิ่มรูปแบบให้กับเมธอด onPoiClick()

b9916bca3c367e3.png

7. งาน: เพิ่มการวางซ้อน

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

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

ขั้นตอน: เพิ่มภาพซ้อนทับพื้น

ในงานนี้ คุณจะได้เพิ่มภาพซ้อนทับพื้นในรูปของหุ่นยนต์ Android ลงในตำแหน่งบ้าน

  1. ดาวน์โหลด รูปภาพ Android นี้และบันทึกลงในโฟลเดอร์ res/drawable (ตรวจสอบว่าชื่อไฟล์คือ android.png)

61fabd56a0841b44.png

  1. ใน onMapReady() หลังจากเรียกให้ย้ายกล้องไปยังตำแหน่งของบ้านแล้ว ให้สร้างออบเจ็กต์ GroundOverlayOptions
  2. กำหนดออบเจ็กต์ให้กับตัวแปรที่ชื่อ androidOverlay
val androidOverlay = GroundOverlayOptions()
  1. ใช้เมธอด BitmapDescriptorFactory.fromResource() เพื่อสร้างออบเจ็กต์ BitmapDescriptor จากแหล่งข้อมูลรูปภาพที่ดาวน์โหลด
  2. ส่งออบเจ็กต์ BitmapDescriptor ที่ได้ไปยังเมธอด image() ของออบเจ็กต์ GroundOverlayOptions
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
  1. สร้าง float overlaySize สำหรับความกว้างเป็นเมตรของภาพซ้อนทับที่ต้องการ ในตัวอย่างนี้ ความกว้าง 100f จะใช้งานได้ดี

ตั้งค่าพร็อพเพอร์ตี้ position สำหรับออบเจ็กต์ GroundOverlayOptions โดยเรียกใช้เมธอด position() และส่งออบเจ็กต์ homeLatLng และ overlaySize

val overlaySize = 100f
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
   .position(homeLatLng, overlaySize)
  1. เรียกใช้ addGroundOverlay() ในออบเจ็กต์ GoogleMap แล้วส่งออบเจ็กต์ GroundOverlayOptions
map.addGroundOverlay(androidOverlay)
  1. เรียกใช้แอป
  2. เปลี่ยนค่าของ zoomLevel เป็น 18f เพื่อดูรูปภาพ Android เป็นภาพซ้อนทับ

b1b25b0acd6a9807.png

8. งาน: เปิดใช้การติดตามตำแหน่ง

ผู้ใช้มักใช้ Google Maps เพื่อดูตำแหน่งปัจจุบันของตน หากต้องการแสดงตำแหน่งอุปกรณ์บนแผนที่ คุณสามารถใช้เลเยอร์ข้อมูลตำแหน่ง

เลเยอร์ข้อมูลตำแหน่งจะเพิ่มไอคอนตำแหน่งของฉันลงในแผนที่

f317f84dcb3ac3a1.png

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

ในงานนี้ คุณจะเปิดใช้เลเยอร์ข้อมูลตำแหน่ง

ขั้นตอน: ขอสิทธิ์เข้าถึงตำแหน่ง

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

ในขั้นตอนนี้ คุณจะขอสิทธิ์เข้าถึงตำแหน่งและเปิดใช้การติดตามตำแหน่ง

  1. ในไฟล์ AndroidManifest.xml ให้ตรวจสอบว่ามีสิทธิ์ FINE_LOCATION อยู่แล้ว Android Studio แทรกสิทธิ์นี้เมื่อคุณเลือกเทมเพลต Google Maps
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  1. ใน MapsActivity ให้สร้างตัวแปรคลาส REQUEST_LOCATION_PERMISSION
private val REQUEST_LOCATION_PERMISSION = 1
  1. หากต้องการตรวจสอบว่าได้รับสิทธิ์หรือไม่ ให้สร้างเมธอดใน MapsActivity ที่ชื่อ isPermissionGranted() ในวิธีนี้ ให้ตรวจสอบว่าผู้ใช้ได้ให้สิทธิ์แล้วหรือไม่
private fun isPermissionGranted() : Boolean {
  return ContextCompat.checkSelfPermission(
       this,
      Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
  1. หากต้องการเปิดใช้การติดตามตำแหน่งในแอป ให้สร้างเมธอดใน MapsActivity ที่ชื่อ enableMyLocation() ซึ่งไม่ต้องใช้อาร์กิวเมนต์และไม่แสดงผลใดๆ ภายใน ให้มองหาสิทธิ์ ACCESS_FINE_LOCATION หากได้รับสิทธิ์ ให้เปิดใช้เลเยอร์ตำแหน่ง หรือขอสิทธิ์
private fun enableMyLocation() {
   if (isPermissionGranted()) {
       map.isMyLocationEnabled = true 
   }
   else {
       ActivityCompat.requestPermissions(
           this,
           arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
           REQUEST_LOCATION_PERMISSION
       )
   }
}
  1. เรียกใช้ enableMyLocation() จากการเรียกกลับ onMapReady() เพื่อเปิดใช้เลเยอร์ตำแหน่ง
override fun onMapReady(googleMap: GoogleMap) {
   ...
   enableMyLocation()
}
  1. แทนที่เมธอด onRequestPermissionsResult() ตรวจสอบว่า requestCode เท่ากับ REQUEST_LOCATION_PERMISSION หรือไม่ หากเป็นเช่นนั้น แสดงว่าคุณได้รับสิทธิ์แล้ว หากได้รับสิทธิ์ ให้ตรวจสอบด้วยว่าอาร์เรย์ grantResults มี PackageManager.PERMISSION_GRANTED ในช่องแรกหรือไม่ หากเป็นเช่นนั้น โปรดโทรหา enableMyLocation()
override fun onRequestPermissionsResult(
   requestCode: Int,
   permissions: Array<String>,
   grantResults: IntArray) {
   if (requestCode == REQUEST_LOCATION_PERMISSION) {
       if (grantResults.contains(PackageManager.PERMISSION_GRANTED)) {
           enableMyLocation()
       }
   }
}
  1. เรียกใช้แอป ควรมีกล่องโต้ตอบที่ขอสิทธิ์เข้าถึงตำแหน่งของอุปกรณ์ โปรดอนุญาตสิทธิ์

da7e23e00ec762c1.png

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

5b12eda7f467bc2f.png

9. รหัสโซลูชัน

ดาวน์โหลดโค้ดสำหรับ Codelab ที่เสร็จสมบูรณ์

$  git clone https://github.com/googlecodelabs/android-kotlin-geo-maps

หรือจะดาวน์โหลดที่เก็บเป็นไฟล์ ZIP, แตกไฟล์ และเปิดใน Android Studio ก็ได้

10. สรุป

ยินดีด้วย คุณเพิ่มแผนที่ Google ลงในแอป Android Kotlin และจัดรูปแบบแล้ว

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

เอกสารประกอบสำหรับนักพัฒนาแอป Android:

เอกสารอ้างอิง:

12. Codelab ถัดไป

ดูลิงก์ไปยัง Codelab อื่นๆ ในหลักสูตรนี้ได้ที่หน้า Landing Page ของ Codelab Android ขั้นสูงใน Kotlin