1. Sebelum Memulai
Codelab ini akan mengajarkan cara mengintegrasikan Maps SDK for Android dengan aplikasi Anda dan menggunakan fitur intinya dengan membuat aplikasi yang menampilkan peta toko sepeda di San Francisco, CA, AS.
Prasyarat
- Pengetahuan dasar tentang pengembangan Kotlin dan Android
Yang akan Anda lakukan
- Mengaktifkan dan menggunakan Maps SDK for Android untuk menambahkan Google Maps ke aplikasi Android.
- Menambahkan, menyesuaikan, dan mengelompokkan penanda.
- Menggambar polyline dan poligon pada peta.
- Mengontrol titik pandang kamera secara terprogram.
Yang akan Anda perlukan
- Maps SDK for Android
- Akun Google dengan penagihan diaktifkan
- Android Studio 2020.3.1 atau versi yang lebih baru
- Layanan Google Play yang diinstal di Android Studio
- Perangkat Android atau emulator Android yang menjalankan platform Google API berbasis Android 4.2.2 atau versi yang lebih tinggi (lihat Menjalankan aplikasi di Android Emulator untuk langkah-langkah penginstalan.)
2. Memulai persiapan
Untuk langkah pengaktifan berikut , Anda harus mengaktifkan Maps SDK for Android.
Menyiapkan Google Maps Platform
Jika Anda belum memiliki akun Google Cloud Platform dan project dengan penagihan diaktifkan, lihat panduan Memulai Google Maps Platform untuk membuat akun penagihan dan project.
- Di Cloud Console, klik menu drop-down project lalu pilih project yang ingin Anda gunakan untuk codelab ini.
- Aktifkan API dan SDK Google Maps Platform yang diperlukan untuk codelab ini di Google Cloud Marketplace. Untuk melakukannya, ikuti langkah-langkah dalam video ini atau dokumentasi ini.
- Buat kunci API di halaman Kredensial di Cloud Console. Anda dapat mengikuti langkah-langkah dalam video ini atau dokumentasi ini. Semua permintaan ke Google Maps Platform memerlukan kunci API.
3. Mulai cepat
Untuk membantu Anda memulai secepatnya, berikut beberapa kode awal untuk membantu Anda mengikuti codelab ini. Anda dapat langsung ke bagian solusi, namun jika Anda ingin mengikuti semua langkah untuk membuatnya sendiri, baca semuanya.
- Lakukan clone repositori jika sudah menginstal
git
.
git clone https://github.com/googlecodelabs/maps-platform-101-android.git
Atau, Anda dapat mengklik tombol berikut untuk mendownload kode sumber.
- Setelah mendapatkan kode, lanjutkan dan buka project yang ada dalam direktori
starter
di Android Studio.
4. Menambahkan Google Maps
Di bagian ini, Anda akan menambahkan Google Maps sehingga dimuat saat aplikasi diluncurkan.
Menambahkan kunci API
Kunci API yang Anda buat di langkah sebelumnya harus diberikan ke aplikasi sehingga Maps SDK for Android dapat mengaitkan kunci dengan aplikasi Anda.
- Untuk memberikannya, buka file bernama
local.properties
di direktori utama project Anda (level yang sama tempatgradle.properties
dansettings.gradle
berada). - Di file tersebut, tentukan kunci baru
GOOGLE_MAPS_API_KEY
dengan nilainya menjadi kunci API yang Anda buat.
local.properties
GOOGLE_MAPS_API_KEY=YOUR_KEY_HERE
Perhatikan bahwa local.properties
dicantumkan dalam file .gitignore
di repositori Git. Hal ini karena kunci API Anda dianggap sebagai informasi sensitif dan tidak boleh diupload ke kontrol sumber, jika memungkinkan.
- Selanjutnya, untuk mengekspos API Anda agar dapat digunakan di seluruh aplikasi, sertakan plugin Plugin Secrets Gradle untuk Android di file
build.gradle
aplikasi yang terletak di direktoriapp/
dan tambahkan baris berikut dalam blokplugins
:
build.gradle tingkat aplikasi
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
Anda juga perlu memodifikasi file build.gradle
level project untuk menyertakan classpath berikut:
build.gradle tingkat project
buildscript {
dependencies {
// ...
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:1.3.0"
}
}
Plugin ini akan membuat kunci yang telah Anda tentukan dalam file local.properties
tersedia sebagai variabel build dalam file manifes Android dan sebagai variabel dalam class BuildConfig
yang dihasilkan Gradle pada waktu build. Menggunakan plugin ini akan menghapus kode boilerplate yang seharusnya diperlukan untuk membaca properti dari local.properties
, sehingga dapat diakses di seluruh aplikasi Anda.
Menambahkan dependensi Google Maps
- Setelah kunci API Anda bisa diakses dalam aplikasi, langkah berikutnya adalah menambahkan dependensi Maps SDK for Android ke file
build.gradle
aplikasi Anda.
Dalam project permulaan yang disertakan bersama codelab ini, dependensi ini telah ditambahkan untuk Anda.
build.gradle
dependencies {
// Dependency to include Maps SDK for Android
implementation 'com.google.android.gms:play-services-maps:17.0.0'
}
- Selanjutnya, tambahkan tag
meta-data
baru diAndroidManifest.xml
untuk meneruskan kunci API yang Anda buat di langkah sebelumnya. Untuk melakukannya, lanjutkan dan buka file ini di Android Studio lalu tambahkan tagmeta-data
berikut dalam objekapplication
di fileAndroidManifest.xml
, yang terletak diapp/src/main
.
AndroidManifest.xml
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${GOOGLE_MAPS_API_KEY}" />
- Selanjutnya, buat file tata letak baru yang bernama
activity_main.xml
di direktoriapp/src/main/res/layout/
dan tentukan sebagai berikut:
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>
Tata letak ini memiliki satu FrameLayout
yang berisi SupportMapFragment
. Fragmen ini berisi objek GoogleMaps
dasar yang Anda gunakan di langkah-langkah selanjutnya.
- Terakhir, update class
MainActivity
yang berada diapp/src/main/java/com/google/codelabs/buildyourfirstmap
dengan menambahkan kode berikut untuk mengganti metodeonCreate
agar Anda dapat menetapkan kontennya dengan tata letak baru yang baru saja Anda buat.
MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
- Sekarang, lanjutkan dan jalankan aplikasi. Anda akan melihat beban peta di layar perangkat Anda.
5. Penataan gaya peta berbasis cloud (Opsional)
Anda dapat menyesuaikan gaya peta menggunakan Penataan gaya peta berbasis cloud.
Membuat ID Peta
Jika Anda belum membuat ID peta dengan gaya peta terkait, lihat panduan ID Peta untuk menyelesaikan langkah-langkah berikut:
- Membuat ID peta.
- Mengaitkan ID peta ke gaya peta.
Menambahkan ID Peta ke aplikasi Anda
Untuk menggunakan ID peta yang Anda buat, ubah file activity_main.xml
dan teruskan ID peta Anda di atribut map:mapId
dari 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" />
Setelah menyelesaikannya, jalankan aplikasi untuk melihat peta dengan gaya yang Anda pilih.
6. Menambahkan penanda
Dalam tugas ini, Anda menambahkan penanda ke peta yang mewakili lokasi menarik yang ingin Anda sorot pada peta. Pertama, Anda mengambil daftar tempat yang telah diberikan dalam project permulaan untuk Anda, kemudian tambahkan tempat tersebut ke peta. Dalam contoh ini digunakan toko sepeda.
Mendapatkan referensi untuk GoogleMap
Pertama, Anda perlu mendapatkan referensi untuk objek GoogleMap
sehingga dapat menggunakan metodenya. Untuk melakukannya, tambahkan kode berikut ke metode MainActivity.onCreate()
Anda, tepat setelah panggilan ke setContentView()
:
MainActivity.onCreate()
val mapFragment = supportFragmentManager.findFragmentById(
R.id.map_fragment
) as? SupportMapFragment
mapFragment?.getMapAsync { googleMap ->
addMarkers(googleMap)
}
Penerapan awalnya akan menemukan SupportMapFragment
yang Anda tambahkan di langkah sebelumnya dengan menggunakan metode findFragmentById()
pada objek SupportFragmentManager
. Setelah referensi diperoleh, panggilan getMapAsync()
dipanggil dan diikuti dengan meneruskan lambda. Lambda ini adalah tempat objek GoogleMap
diteruskan. Di dalam lambda ini, panggilan metode addMarkers()
dipanggil, yang didefinisikan dalam waktu singkat.
Class yang disediakan: PlacesReader
Dalam project permulaan, class PlacesReader
telah disediakan untuk Anda. Class ini membaca daftar berisi 49 tempat yang disimpan dalam file JSON bernama places.json
dan menampilkannya sebagai List<Place>
. Tempat-tempat itu sendiri mewakili daftar toko sepeda di sekitar San Francisco, CA, AS.
Jika penasaran dengan penerapan class ini, Anda dapat mengaksesnya di GitHub atau membuka class PlacesReader
di 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()
}
}
Memuat tempat
Untuk memuat daftar toko sepeda, tambahkan properti di MainActivity
yang bernama places
dan tentukan sebagai berikut:
MainActivity.places
private val places: List<Place> by lazy {
PlacesReader(this).read()
}
Kode ini memanggil metode read()
pada PlacesReader
, yang menampilkan List<Place>
. Place
memiliki properti yang disebut name
, nama tempat tersebut, dan latLng
, koordinat tempat tersebut.
Tempat
data class Place(
val name: String,
val latLng: LatLng,
val address: LatLng,
val rating: Float
)
Menambahkan penanda ke peta
Setelah daftar tempat dimuat ke memori, langkah berikutnya adalah menampilkan tempat ini pada peta.
- Buat metode di
MainActivity
yang bernamaaddMarkers()
dan tentukan sebagai berikut:
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)
)
}
}
Metode ini melakukan iterasi melalui daftar places
yang diikuti dengan memanggil metode addMarker()
di objek GoogleMap
yang disediakan. Penanda tersebut dibuat dengan membuat instance objek MarkerOptions
, yang memungkinkan Anda menyesuaikan penanda itu sendiri. Dalam hal ini, judul dan posisi penanda diberikan, yang masing-masing mewakili nama toko sepeda dan koordinatnya.
- Lanjutkan dan jalankan aplikasi, lalu buka San Francisco untuk melihat penanda yang baru saja Anda tambahkan.
7. Menyesuaikan penanda
Ada beberapa opsi penyesuaian untuk penanda yang baru saja Anda tambahkan untuk membantu menonjolkan penanda dan memberikan informasi yang berguna kepada pengguna. Dalam tugas ini, Anda akan menjelajahi beberapa di antaranya dengan menyesuaikan gambar setiap penanda serta jendela informasi yang ditampilkan saat penanda diketuk.
Menambahkan jendela info
Secara default, jendela info saat Anda mengetuk penanda akan menampilkan judul dan cuplikannya (jika ditetapkan). Anda menyesuaikannya sehingga dapat menampilkan informasi tambahan, seperti alamat dan rating tempat.
Membuat marker_info_contents.xml
Pertama, buat file tata letak baru bernama marker_info_contents.xml
.
- Untuk melakukannya, klik kanan pada folder
app/src/main/res/layout
di tampilan project di Android Studio lalu pilih New > Layout Resource File.
- Pada dialog, ketik
marker_info_contents
di kolom File name danLinearLayout
di kolomRoot element
, lalu klik OK.
File tata letak ini kemudian di-inflate untuk menampilkan konten dalam jendela info.
- Salin konten dalam cuplikan kode berikut, yang menambahkan tiga
TextViews
dalam kelompok tampilanLinearLayout
vertikal, dan menimpa kode default dalam file.
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>
Membuat penerapan InfoWindowAdapter
Setelah membuat file tata letak untuk jendela info kustom, langkah berikutnya adalah menerapkan antarmuka GoogleMap.InfoWindowAdapter. Antarmuka ini berisi dua metode, getInfoWindow()
dan getInfoContents()
. Kedua metode ini menampilkan objek View
opsional, yang pertama digunakan untuk menyesuaikan jendela itu sendiri, sementara yang kedua untuk menyesuaikan kontennya. Dalam kasus ini, Anda menerapkan keduanya dan menyesuaikan tampilan getInfoContents()
saat menampilkan null di getInfoWindow()
, yang menunjukkan bahwa jendela default harus digunakan.
- Buat file Kotlin baru yang bernama
MarkerInfoWindowAdapter
dalam paket yang sama sepertiMainActivity
dengan mengklik kanan folderapp/src/main/java/com/google/codelabs/buildyourfirstmap
dalam tampilan project di Android Studio, lalu pilih New > Kotlin File/Class.
- Pada dialog, ketik
MarkerInfoWindowAdapter
dan biarkan File tetap disorot.
- Setelah Anda membuat file tersebut, salin konten dalam cuplikan kode berikut ke file baru Anda.
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
}
}
Dalam konten metode getInfoContents()
, Penanda yang disediakan dalam metode ini ditransmisikan ke jenis Place
, dan jika transmisi tidak memungkinkan, metode akan menampilkan null (Anda belum menetapkan properti tag pada Marker
, tetapi Anda dapat melakukannya di langkah berikutnya).
Selanjutnya, tata letak marker_info_contents.xml
di-inflate, lalu teks yang berisi TextViews
ditetapkan ke tag Place
.
Mengupdate MainActivity
Untuk menempelkan semua komponen yang telah Anda buat sejauh ini, Anda perlu menambahkan dua baris di class MainActivity
.
Pertama, untuk meneruskan InfoWindowAdapter
, MarkerInfoWindowAdapter
kustom di dalam panggilan metode getMapAsync
, panggil metode setInfoWindowAdapter()
pada objek GoogleMap
lalu buat instance baru MarkerInfoWindowAdapter
.
- Lakukan hal ini dengan menambahkan kode berikut setelah panggilan metode
addMarkers()
di dalam lambdagetMapAsync()
.
MainActivity.onCreate()
// Set custom info window adapter
googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
Terakhir, Anda harus menetapkan setiap Place sebagai properti tag pada setiap Penanda yang ditambahkan ke peta.
- Untuk melakukannya, ubah panggilan
places.forEach{}
dalam fungsiaddMarkers()
dengan yang berikut:
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
}
Menambahkan gambar penanda kustom
Menyesuaikan gambar penanda adalah salah satu cara seru untuk mengomunikasikan jenis tempat yang diwakili penanda pada peta Anda. Untuk langkah ini, Anda menampilkan sepeda, bukan penanda merah default untuk mewakili setiap toko pada peta. Project permulaan mencakup ikon sepeda ic_directions_bike_black_24dp.xml
di app/src/res/drawable
, yang Anda gunakan.
Menetapkan bitmap kustom pada penanda
Dengan ikon sepeda vektor drawable, langkah berikutnya adalah menetapkan drawable tersebut sebagai ikon penanda masing-masing pada peta. MarkerOptions
memiliki metode icon
, yang menyertakan BitmapDescriptor
yang Anda gunakan untuk melakukan ini.
Pertama-tama, Anda perlu mengonversi vektor drawable yang baru saja ditambahkan ke BitmapDescriptor
. File bernama BitMapHelper
yang disertakan dalam project permulaan berisi fungsi bantuan yang disebut vectorToBitmap()
, yang melakukan hal tersebut.
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)
}
}
Metode ini menyertakan Context
, sebuah ID resource drawable, serta integer warna, dan membuat representasi BitmapDescriptor
dari keduanya.
Dengan menggunakan metode bantuan, deklarasikan properti baru yang disebut bicycleIcon
dan berikan definisi berikut: 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)
}
Properti ini menggunakan warna standar colorPrimary
di aplikasi Anda, serta menggunakannya untuk memberikan warna pada ikon sepeda dan menampilkannya sebagai BitmapDescriptor
.
- Menggunakan properti ini, lanjutkan dan panggil metode
icon
dariMarkerOptions
di metodeaddMarkers()
untuk menyelesaikan penyesuaian ikon. Setelah melakukan ini, properti penanda seharusnya terlihat seperti berikut:
MainActivity.addMarkers()
val marker = googleMap.addMarker(
MarkerOptions()
.title(place.name)
.position(place.latLng)
.icon(bicycleIcon)
)
- Jalankan aplikasi untuk melihat penanda yang telah diupdate.
8 Penanda cluster
Bergantung pada tingkat zoom yang digunakan pada peta, Anda mungkin melihat bahwa penanda yang Anda tambahkan tumpang-tindih. Sangat sulit untuk berinteraksi dengan penanda yang tumpang-tindih karena menghasilkan banyak derau, yang akan berdampak pada kegunaan aplikasi Anda.
Untuk meningkatkan kualitas pengalaman pengguna terkait hal ini, setiap kali Anda memiliki set data besar yang dikelompokkan berdekatan, praktik terbaiknya adalah menerapkan pengelompokan penanda. Dengan pengelompokan, saat Anda memperbesar dan memperkecil peta, penanda yang letaknya berdekatan akan dikelompokkan bersama seperti ini:
Untuk menerapkan ini, Anda memerlukan bantuan Library Utilitas Maps SDK for Android.
Library Utilitas Maps SDK for Android
Library Utilitas Maps SDK for Android dibuat sebagai cara untuk memperluas fungsi Maps SDK for Android. Library ini menawarkan fitur lanjutan, seperti pengelompokan penanda, peta panas, dukungan KML dan GeoJson, encoding dan decoding polyline, serta beberapa fungsi bantuan di sekeliling geometri bundar.
Mengupdate build.gradle
Karena library utilitas dipaketkan secara terpisah dari Maps SDK for Android, Anda perlu menambahkan dependensi tambahan ke file build.gradle
.
- Lanjutkan dan update bagian
dependencies
pada fileapp/build.gradle
Anda.
build.gradle
implementation 'com.google.maps.android:android-maps-utils:1.1.0'
- Setelah menambahkan baris ini, Anda harus melakukan sinkronisasi project untuk mengambil dependensi baru.
Menerapkan pengelompokan
Untuk menerapkan pengelompokan di aplikasi Anda, ikuti tiga langkah berikut:
- Terapkan antarmuka
ClusterItem
. - Buat subclass untuk class
DefaultClusterRenderer
. - Buat
ClusterManager
dan tambahkan item.
Menerapkan antarmuka ClusterItem
Semua objek yang mewakili penanda yang dapat dikelompokkan pada peta perlu menerapkan antarmuka ClusterItem
. Dalam kasus Anda, ini artinya model Place
harus sesuai dengan ClusterItem
. Lanjutkan dan buka file Place.kt
lalu lakukan perubahan berikut:
Tempat
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 menentukan tiga metode ini:
getPosition()
, yang mewakiliLatLng
tempat.getTitle()
, yang mewakili nama tempatgetSnippet()
, yang mewakili alamat tempat.
Membuat subclass untuk class DefaultClusterRenderer
Class yang bertugas untuk menerapkan pengelompokan, ClusterManager
, secara internal menggunakan class ClusterRenderer
untuk menangani pembuatan cluster saat Anda menggeser dan melakukan zoom di sekeliling peta. Secara default disertakan perender default, DefaultClusterRenderer
, yang menerapkan ClusterRenderer
. Untuk kasus sederhana, ini seharusnya sudah cukup. Namun pada kasus Anda, karena penanda perlu disesuaikan, Anda harus memperluas class ini dan menambahkan penyesuaian di dalamnya.
Lanjutkan dan buat file Kotlin PlaceRenderer.kt
dalam paket com.google.codelabs.buildyourfirstmap.place
lalu tentukan sebagai berikut:
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
}
}
Class ini mengganti dua fungsi berikut:
onBeforeClusterItemRendered()
, yang dipanggil sebelum cluster dirender pada peta. Di sini, Anda dapat memberikan penyesuaian melaluiMarkerOptions
—pada kasus ini akan ditetapkan judul, posisi, dan ikon penanda.onClusterItemRenderer()
, yang dipanggil tepat setelah penanda dirender pada peta. Ini adalah tempat Anda dapat mengakses objekMarker
yang dibuat—pada kasus ini akan ditetapkan properti tag penanda.
Membuat ClusterManager dan menambahkan item
Terakhir, agar pengelompokan berfungsi, Anda perlu memodifikasi MainActivity
untuk membuat instance ClusterManager
dan memberikan dependensi yang diperlukan. ClusterManager
menangani penambahan penanda (objek ClusterItem
) secara internal, jadi bukannya menambahkan penanda langsung ke peta, tanggung jawab ini didelegasikan ke ClusterManager
. Selain itu, ClusterManager
juga memanggil setInfoWindowAdapter()
secara internal, sehingga menyetel jendela info kustom harus dilakukan pada objek MarkerManager.Collection
ClusterManger
.
- Untuk memulai, ubah konten lambda dalam panggilan
getMapAsync()
diMainActivity.onCreate()
. Lanjutkan dan jadikan sebagai komentar panggilan keaddMarkers()
dansetInfoWindowAdapter()
, dan bukannya memanggil metode yang disebutaddClusteredMarkers()
, yang Anda tentukan berikutnya.
MainActivity.onCreate()
mapFragment?.getMapAsync { googleMap ->
//addMarkers(googleMap)
addClusteredMarkers(googleMap)
// Set custom info window adapter.
// googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
}
- Selanjutnya, di
MainActivity
, tentukanaddClusteredMarkers()
.
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()
}
}
Metode ini membuat instance ClusterManager
, meneruskan perender kustom PlacesRenderer
ke lokasi ini, menambahkan semua tempat, dan memanggil metode cluster()
. Selain itu, karena ClusterManager
menggunakan metode setInfoWindowAdapter()
pada objek peta, menyetel jendela info kustom harus dilakukan pada objek ClusterManager.markerCollection
. Terakhir, karena Anda ingin pengelompokan berubah saat pengguna menggeser dan melakukan zoom di sekeliling peta, OnCameraIdleListener
disediakan untuk googleMap
, agar clusterManager.onCameraIdle()
dipanggil saat tidak ada aktivitas kamera.
- Lanjutkan dan jalankan aplikasi untuk melihat toko baru yang dikelompokkan.
9. Gambar di peta
Meskipun Anda telah mempelajari satu cara untuk menggambar di peta (dengan menambahkan penanda), SDK Maps for Android mendukung berbagai cara lain untuk menggambar agar dapat menampilkan informasi yang berguna pada peta.
Misalnya, jika ingin menampilkan rute dan area pada peta, Anda dapat menggunakan polyline dan poligon untuk menampilkannya pada peta. Atau, jika Anda ingin memperbaiki gambar pada permukaan bumi, Anda dapat menggunakan overlay bumi.
Dalam tugas ini, Anda akan mempelajari cara menggambar bentuk, khususnya lingkaran, di sekeliling penanda setiap kali penanda diketuk.
Menambahkan pemroses klik
Biasanya, cara Anda menambahkan pemroses klik ke penanda adalah dengan meneruskan pemroses klik langsung pada objek GoogleMap
melalui setOnMarkerClickListener()
. Namun, karena Anda menggunakan pengelompokan, pemroses klik harus diberikan ke ClusterManager
.
- Pada metode
addClusteredMarkers()
diMainActivity
, lanjutkan dan tambahkan baris berikut tepat setelah panggilan kecluster()
.
MainActivity.addClusteredMarkers()
// Show polygon
clusterManager.setOnClusterItemClickListener { item ->
addCircle(googleMap, item)
return@setOnClusterItemClickListener false
}
Metode ini menambahkan pemroses dan memanggil metode addCircle()
, yang Anda tentukan berikutnya. Terakhir, false
ditampilkan dari metode ini untuk menunjukkan bahwa metode ini tidak menggunakan peristiwa ini.
- Selanjutnya, Anda perlu menentukan properti
circle
dan metodeaddCircle()
diMainActivity
.
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))
)
}
Properti circle
ditetapkan sehingga setiap kali penanda baru diketuk, lingkaran sebelumnya akan dihapus dan lingkaran baru ditambahkan. Perhatikan bahwa API untuk menambahkan lingkaran agak mirip dengan API untuk menambahkan penanda.
- Lanjutkan sekarang dan jalankan aplikasi untuk melihat perubahan.
10. Kontrol Kamera
Sebagai tugas terakhir Anda, lihat beberapa kontrol kamera sehingga Anda dapat berfokus pada tampilan di sekitar wilayah tertentu.
Kamera dan tampilan
Jika saat menjalankan aplikasi, Anda melihat kamera menampilkan benua Afrika dan Anda harus menggeser dan men-zoom ke San Francisco dengan sangat cermat untuk menemukan penanda yang Anda tambahkan. Meskipun bisa menjadi cara yang menyenangkan untuk menjelajahi dunia, ini tidak berguna jika Anda ingin langsung menampilkan penanda.
Untuk membantu mengatasi hal ini, Anda dapat menetapkan posisi kamera secara terprogram sehingga tampilan akan dipusatkan di tempat yang Anda inginkan.
- Lanjutkan dan tambahkan kode berikut ke panggilan
getMapAsync()
untuk menyesuaikan tampilan kamera sehingga akan diinisialisasi ke San Francisco saat aplikasi dibuka.
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))
}
}
Pertama, setOnMapLoadedCallback()
dipanggil sehingga kamera diupdate hanya setelah peta dimuat. Langkah ini diperlukan karena properti peta, seperti dimensi, harus dihitung sebelum melakukan panggilan update kamera.
Di lambda, objek LatLngBounds
baru akan dibuat, yang menentukan wilayah persegi panjang pada peta. Ini dibuat secara bertahap dengan menyertakan semua nilai LatLng
tempat di dalamnya untuk memastikan semua tempat berada dalam batas. Setelah objek ini dibuat, metode moveCamera()
di GoogleMap
dipanggil dan CameraUpdate
diberikan melalui CameraUpdateFactory.newLatLngBounds(bounds.build(), 20)
.
- Jalankan aplikasi dan perhatikan bahwa kamera sekarang diinisialisasi di San Francisco.
Memproses perubahan kamera
Selain mengubah posisi kamera, Anda juga dapat memproses update kamera saat pengguna bergerak di sekeliling peta. Ini dapat berguna jika Anda ingin mengubah UI saat kamera bergerak berkeliling.
Untuk hiburan saja, Anda memodifikasi kode untuk membuat penanda transparan setiap kali kamera bergerak.
- Pada metode
addClusteredMarkers()
, lanjutkan dan tambahkan baris berikut di bagian bawah metode:
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 }
}
Ini menambahkan OnCameraMoveStartedListener
sehingga setiap kali kamera mulai bergerak, semua nilai alfa penanda (cluster dan penanda) akan diubah menjadi 0.3f
sehingga penanda tampak transparan.
- Terakhir, untuk memodifikasi penanda agar tidak lagi transparan saat kamera berhenti, ubah konten
setOnCameraIdleListener
dalam metodeaddClusteredMarkers()
menjadi:
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()
}
- Lanjutkan dan jalankan aplikasi untuk melihat hasilnya.
11. KTX Maps
Untuk aplikasi Kotlin yang menggunakan satu atau beberapa Android SDK Google Maps Platform, ekstensi Kotlin atau library KTX tersedia untuk memungkinkan Anda memanfaatkan fitur bahasa Kotlin seperti coroutine, properti/fungsi ekstensi, dan lainnya. Setiap Google Maps SDK memiliki library KTX yang sesuai seperti yang ditunjukkan di bawah ini:
Dalam tugas ini, Anda akan menggunakan library KTX Maps dan Utilitas Maps Maps untuk aplikasi Anda dan memfaktorkan ulang implementasi tugas sebelumnya sehingga Anda dapat menggunakan fitur bahasa khusus Kotlin di aplikasi Anda.
- Sertakan dependensi KTX dalam file build.gradle tingkat aplikasi
Karena aplikasi menggunakan Maps SDK for Android dan Library Utilitas Maps SDK for Android, Anda harus menyertakan library KTX yang sesuai untuk library ini. Anda juga akan menggunakan fitur yang ditemukan dalam library KTX Siklus Proses AndroidX dalam tugas ini, jadi sertakan juga dependensi tersebut dalam file build.gradle
level aplikasi Anda.
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'
}
- Menggunakan fungsi ekstensi GoogleMap.addMarker() dan GoogleMap.addCircle()
Library KTX Maps menyediakan alternatif API gaya DSL untuk GoogleMap.addMarker(MarkerOptions)
dan GoogleMap.addCircle(CircleOptions)
yang digunakan pada langkah sebelumnya. Untuk menggunakan API yang disebutkan di atas, pembuatan class yang berisi opsi untuk penanda atau lingkaran diperlukan, sedangkan dengan alternatif KTX, Anda dapat menyetel opsi penanda atau lingkaran dalam lambda yang Anda berikan.
Untuk menggunakan API ini, perbarui metode MainActivity.addMarkers(GoogleMap)
dan 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))
}
}
Menulis ulang metode di atas dengan cara ini jauh lebih ringkas untuk dibaca yang dimungkinkan menggunakan literal fungsi dengan penerima Kotlin.
- Menggunakan fungsi penangguhan ekstensi SupportMapFragment.awaitMap() dan GoogleMap.awaitMapLoad()
Library KTX Maps juga menyediakan ekstensi fungsi penangguhan untuk digunakan dalam coroutine. Secara khusus, ada alternatif fungsi yang ditangguhkan untuk SupportMapFragment.getMapAsync(OnMapReadyCallback)
dan GoogleMap.setOnMapLoadedCallback(OnMapLoadedCallback)
. Menggunakan API alternatif ini meniadakan kebutuhan untuk meneruskan callback dan sebagai gantinya Anda dapat menerima respons metode-metode ini secara seri dan sinkron.
Karena metode ini menangguhkan fungsi, penggunaannya harus dilakukan dalam coroutine. Library Lifecycle Runtime KTX menawarkan ekstensi untuk menyediakan cakupan coroutine berbasis siklus proses sehingga coroutine dijalankan dan dihentikan pada peristiwa siklus proses yang sesuai.
Dengan menggabungkan konsep-konsep ini, perbarui metode MainActivity.onCreate(Bundle)
:
MainActivity.onCreate(Paket)
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)
}
}
Cakupan coroutine lifecycleScope.launchWhenCreated
akan menjalankan blok saat aktivitas berada setidaknya dalam status yang dibuat. Perhatikan juga bahwa panggilan untuk mengambil objek GoogleMap
, dan menunggu peta selesai dimuat, masing-masing telah diganti dengan SupportMapFragment.awaitMap()
dan GoogleMap.awaitMapLoad()
. Memfaktorkan ulang kode menggunakan fungsi penangguhan ini memungkinkan Anda menulis kode berbasis callback yang setara secara berurutan.
- Lanjutkan dan build ulang aplikasi dengan perubahan yang telah difaktorkan ulang.
12. Selamat
Selamat! Anda telah mempelajari begitu banyak konten dan semoga Anda memiliki pemahaman yang lebih baik tentang fitur inti yang ditawarkan di Maps SDK for Android.
Pelajari lebih lanjut
- Places SDK for Android—eksplorasi berbagai data tempat untuk menemukan bisnis di sekeliling Anda.
- android-maps-ktx—sebuah library open source yang memungkinkan Anda terintegrasi dengan Maps SDK for Android dan Library Utilitas Maps SDK for Android dengan cara yang kompatibel dengan Kotlin.
- android-place-ktx—sebuah library open source yang memungkinkan Anda terintegrasi dengan Places SDK for Android dengan cara yang kompatibel dengan Kotlin.
- android-sample—kode contoh di GitHub yang menunjukkan semua fitur yang dibahas dalam codelab ini dan lainnya.
- Codelab Kotlin lainnya untuk membuat aplikasi Android dengan Google Maps Platform