หลังจากที่ได้ API Key มาจาก Google Developer Console แล้ว ต่อไปก็ถึงเวลาของเรียกใช้งาน Maps SDK เพื่อแสดง Google Maps ในโปรเจคแอนดรอยด์แล้วล่ะ

บทความในซีรีย์เดียวกัน

Package Name จะต้องตรงกับที่กำหนดไว้ใน Google Developer Console

อย่างที่บอกไปในบทความที่แล้ว ว่าการขอ API Key จะต้องกำหนด SHA1 และ Package Name ของแอปให้ถูกต้อง ดังนั้นโปรเจคที่จะนำ API Key ไปใช้ ก็จะต้องกำหนด Package Name ให้ตรงด้วยเช่นกัน โดยสามารถเช็คได้จาก applicationId ที่กำหนดไว้ใน build.gradle ของโปรเจค

// build.gradle (Module: app)
android {
    /* ... */
    defaultConfig {
        applicationId "com.akexorcist.drivingnavigator"
        /* ... */
    }
}
/* ... */

เพิ่ม Maps SDK เข้าไปในโปรเจค

สำหรับ Maps SDK v3 นี้จะแตกต่างจาก v2 ตรงที่ไม่จำเป็นต้องเพิ่ม Dependency ของ Google Play Services อีกต่อไปแล้ว เพิ่มแค่ Dependency ของ Maps SDK v3 ตัวล่าสุดลงไปใน build.gradle ได้เลย

// build.gradle (Module: app)
/* ... */
dependencies {
    /* ... */
    implementation 'com.google.android.libraries.maps:maps:3.1.0-beta'
}

จากนั้นก็ Sync Gradle อีกครั้งเป็นอันเรียบร้อย

กำหนด API Key ลงไปในโปรเจค

ในการกำหนด API Key นั้นจะต้องกำหนดใน Android Manifest ด้วย <meta-data>ที่ชื่อว่า com.google.android.geo.API_KEY ซึ่งเป็น Metadata ที่ Maps SDK กำหนดไว้เพื่อดึง API Key ไปใช้งาน

<!-- AndroidManifest.xml -->
<?xml version="1.0" encoding="utf-8"?>
<manifest ...
    package="com.akexorcist.drivingnavigator">

    <application ...>
        <!-- ... -->
        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="AIzaSyDWGQhKXxJpEl_Juqhdx4XRmZSYGSV6vCE" />
    </application>

</manifest>

นักพัฒนาจะกำหนดเป็น String Value ลงไปตรงๆเลยก็ได้ หรือจะสร้างเป็น String Resource เพื่อใช้แยก API Key ตาม Build Variant ก็ได้เช่นกัน

การเพิ่ม Google Maps เข้าไปใน Layout Resource

สำหรับการแสดง Google Maps นั้นจะมีอยู่ 2 แบบด้วยกันคือ

  • Fragment : com.google.android.libraries.maps.SupportMapFragment
  • View : com.google.android.libraries.maps.MapView

ซึ่งเจ้าของบล็อกแนะนำให้ใช้แบบ Fragment แทน เพราะว่ามันจะช่วยจัดการเรื่อง Lifecycle ให้โดยอัตโนมัติ ในขณะที่การใช้แบบ View จะต้องจัดการกับ Lifecycle เองทั้งหมด (เบื้องหลังของแบบ Fragment ก็ใช้ MapView นี่ล่ะ แต่จัดการ Lifecycle ให้เรียบร้อยแล้ว)

ดังนั้นการเพิ่ม Google Maps เข้าไปใน Layout Resource ก็จะต้องใช้ <fragment> ซึ่งเป็นของ Fragment นั่นเอง

<!-- activity_main.xml -->
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout ...>

    <fragment
        android:id="@+id/mapFragment"
        android:name="com.google.android.libraries.maps.SupportMapFragment"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

โดยมีจุดสำคัญคือจะต้องกำหนด android:id เป็นอะไรก็ได้ เพื่อเรียกใช้งานผ่าน Kotlin ในภายหลัง และจะต้องกำหนด android:name เป็น com.google.android.libraries.maps.SupportMapFragment

เพราะว่า SupportMapFragment ก็เสมือน View ตัวหนึ่ง ดังนั้นผู้ที่หลงเข้ามาอ่านจึงสามารถจัด Layout ได้ตามใจชอบ เพื่อให้ตรงกับ UI ที่ต้องการ

เรียกใช้งาน Google Maps บน Activity/Fragment

เนื่องจากเจ้าของบล็ฮกเรียกใช้เป็น SupportMapFragment ดังนั้นเวลาที่จะเรียกใช้งานใน Activity หรือ Fragment ก็จะใช้คำสั่ง findFragmentById(id: Int) เพื่อค้นหา Fragment ด้วย ID ของ View นั่นเอง

ซึ่งสามารถเขียนเป็น Function ได้ง่ายๆแบบนี้

private fun getMapFragment(fragmentManager: FragmentManager): SupportMapFragment? =
        fragmentManager.findFragmentById(R.id.mapFragment) as? SupportMapFragment

สำหรับ Activity

ในกรณีที่เรียกคำสั่งดังกล่าวใน Activity ก็จะต้องใช้เป็น SupportFragmentManager

// MainActivity.kt
class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        /* ... */
        initGoogleMap()
    }
    
    private fun initGoogleMap() {
        getMapFragment(supportFragmentManager)?.let { mapFragment: SupportMapFragment ->
            // Do something with SupportMapFragment
        }
    }

    private fun getMapFragment(fragmentManager: FragmentManager): SupportMapFragment? =
        fragmentManager.findFragmentById(R.id.mapFragment) as? SupportMapFragment
}

สำหรับ Fragment

ถ้าเรียกคำสั่งดังกล่าวใน Fragment ก็จะใช้เป็น FragmentManager ของ Fragment เองได้เลย

// MainFragment.kt
class MainFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        initGoogleMap()
    }
    
    private fun initGoogleMap() {
        getMapFragment(requireFragmentManager())?.let { mapFragment: SupportMapFragment ->
			// Do something with SupportMapFragment
        }
    }

    private fun getMapFragment(fragmentManager: FragmentManager): SupportMapFragment? =
        fragmentManager.findFragmentById(R.id.mapFragment) as? SupportMapFragment
}

เตรียม Google Map ให้พร้อมใช้งาน

เมื่อเรียก SupportMapFragment ได้แล้ว สิ่งที่ต้องทำเป็นอันดับต่อไปก็คือการสั่งให้ Google Maps เริ่มทำงานด้วยคำสั่ง getMapAsync(callback: OnMapReadyCallback)

val mapFragment: SupportMapFragment = /* ... */
mapFragment.getMapAsync { googleMap: GoogleMap ->
    // Google Maps is ready
}

เพราะการที่ Google Maps จะพร้อมใช้งานมันต้องใช้เวลา ดังนั้นคำสั่งดังกล่าวจึงทำงานในลักษณะของ Asynchronous เพื่อส่งเป็น Callback มาบอกให้รู้ว่า Google Maps พร้อมใช้งานแล้ว

และจะเห็นว่า Callback ที่ได้นั้นมีการส่งคลาส GoogleMap มาให้ด้วย ซึ่งเป็นคลาสหลักสำหรับการควบคุมการทำงานของ Google Maps นั่นเอง ดังนั้นให้เก็บค่าดังกล่าวเป็น Global Variable ไว้ได้เลย

private var googleMap: GoogleMap? = null
/* ... */
private fun initGoogleMap() {
    getMapFragment(supportFragmentManager)?.let { mapFragment ->
        mapFragment.getMapAsync { googleMap: GoogleMap ->
            onMapReady(googleMap)
        }
    }
}
  
private fun onMapReady(googleMap: GoogleMap) {
    this.googleMap = googleMap
    // Setup the Google Map as you want
}

จากนั้นก็ให้ลองทดสอบดูก่อน ว่า Google Maps แสดงขึ้นมาบนหน้าจอถูกต้องหรือไม่ เพราะคำสั่งทั้งหมดในบทความนี้เป็นคำสั่งพื้นฐานสำหรับการเรียกใช้งาน Google Maps

Google Maps ไม่แสดง?

เมื่อใดก็ตามที่ Google Maps ไม่แสดงในแอป จะมี Error Log แสดงให้ทราบใน Logcat เสมอ ซึ่งส่วนใหญ่มักจะมีปัญหาจากการกำหนด API Key ผิด หรือไม่ก็กำหนด Package Name หรือ SHA1 ผิด

ดังนั้นให้ลองตรวจสอบข้อมูลที่แสดงใน Error Log ให้ดีๆว่าข้อมูลที่กำหนดไว้ใน Google Developer Console นั้นถูกต้องหรือไม่

2020-11-22 00:10:57.305 14161-14262/com.akexorcist.drivingnavigator E/Google Maps SDK for Android: Authorization failure.  Please see https://developers.google.com/maps/documentation/android-sdk/start for how to correctly set up the map.
2020-11-22 00:10:57.307 14161-14262/com.akexorcist.drivingnavigator E/Google Maps SDK for Android: In the Google Developer Console (https://console.developers.google.com)
    Ensure that the "Maps SDK for Android" is enabled.
    Ensure that the following Android Key exists:
    	API Key: AIzaSyDWGQhKXxJpEl_Juqhdx4XRmZSYGSV6vC3
    	Android Application (<cert_fingerprint>;<package_name>): DE:96:08:AC:0E:12:43:46:B0:E9:CB:EA:18:55:6C:69:04:C3:F3:CE
;com.akexorcist.drivingnavigator

เมื่อแก้ไขทุกอย่างให้ถูกต้องแล้วก็ให้ลองทดสอบแอปดูใหม่อีกครั้ง ในกรณีที่มีการแก้ไขข้อมูลบน Google Developer Console ก็อาจจะต้องรอซัก 2-3 นาที เพื่อให้ข้อมูลอัปเดตล่าสุด

ในที่สุด Google Maps ก็พร้อมใช้งานแล้ว

จะเห็นว่าการเริ่มต้นเรียกใช้งาน Google Maps นั้นมีขั้นตอนที่ง่ายมาก ทั้งนี้ก็เพราะ Maps SDK เตรียมไว้ให้ทั้งหมดแล้วนั่นเอง ต่อไปก็ถึงเวลาของคำสั่งต่างๆที่ใช้ควบคุมการทำงานของ Google Maps แล้ว