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 pegunungan di Colorado, AS, menggunakan berbagai jenis penanda. Selain itu, Anda akan mempelajari cara menggambar bentuk lain di peta.
Tampilannya akan terlihat seperti berikut setelah Anda menyelesaikan codelab:
Prasyarat
- Pengetahuan dasar tentang Kotlin, Jetpack Compose, dan pengembangan Android
Yang akan Anda lakukan
- Mengaktifkan dan menggunakan library Maps Compose untuk Maps SDK for Android guna menambahkan
GoogleMap
ke aplikasi Android - Menambahkan dan menyesuaikan penanda
- Menggambar poligon di peta
- Mengontrol titik pandang kamera secara terprogram
Yang Anda butuhkan
- Maps SDK for Android
- Akun Google dengan penagihan diaktifkan
- Android Studio versi stabil terbaru
- Perangkat Android atau emulator Android yang menjalankan platform Google API berbasis Android 5.0 atau yang lebih tinggi (lihat Menjalankan aplikasi di Android Emulator untuk langkah-langkah penginstalan.)
- Koneksi internet
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, tetapi jika Anda ingin mengikuti semua langkah untuk membuatnya sendiri, baca semuanya.
- Lakukan clone repositori jika sudah menginstal
git
.
git clone https://github.com/googlemaps-samples/codelab-maps-platform-101-compose.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. Tambahkan kunci API Anda ke project
Bagian ini menjelaskan cara menyimpan kunci API sehingga dapat dirujuk dengan aman oleh aplikasi Anda. Anda tidak boleh memasukkan kunci API ke dalam sistem kontrol versi Anda, jadi sebaiknya simpan kunci tersebut dalam file secrets.properties
, yang akan ditempatkan di salinan lokal direktori root project Anda. Untuk informasi selengkapnya tentang file secrets.properties
, lihat File properti Gradle.
Untuk menyederhanakan tugas ini, sebaiknya Anda menggunakan Plugin Secrets Gradle untuk Android.
Untuk menginstal Plugin Secrets Gradle untuk Android di project Google Maps:
- Di Android Studio, buka file
build.gradle.kts
tingkat teratas dan tambahkan kode berikut ke elemendependencies
di bagianbuildscript
.buildscript { dependencies { classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1") } }
- Buka file
build.gradle.kts
tingkat modul dan tambahkan kode berikut ke elemenplugins
.plugins { // ... id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") }
- Di file
build.gradle.kts
tingkat modul, pastikantargetSdk
dancompileSdk
ditetapkan ke minimal 34. - Simpan file dan sinkronkan project Anda dengan Gradle.
- Buka file
secrets.properties
di direktori tingkat teratas, lalu tambahkan kode berikut. GantiYOUR_API_KEY
dengan kunci API Anda. Simpan kunci Anda dalam file ini karenasecrets.properties
tidak di-check in ke dalam sistem kontrol versi.MAPS_API_KEY=YOUR_API_KEY
- Simpan file.
- Buat file
local.defaults.properties
di direktori tingkat teratas, folder yang sama dengan filesecrets.properties
, lalu tambahkan kode berikut. File ini ditujukan untuk menyediakan lokasi cadangan Kunci API jika fileMAPS_API_KEY=DEFAULT_API_KEY
secrets.properties
tidak dapat ditemukan agar build tidak gagal. Hal ini akan terjadi saat Anda meng-clone aplikasi dari sistem kontrol versi dan Anda belum membuat filesecrets.properties
secara lokal untuk memberikan kunci API Anda. - Simpan file.
- Dalam file
AndroidManifest.xml
, bukacom.google.android.geo.API_KEY
, lalu perbarui atributandroid:value
. Jika tag<meta-data>
tidak ada, buat tag tersebut sebagai turunan dari tag<application>
.<meta-data android:name="com.google.android.geo.API_KEY" android:value="${MAPS_API_KEY}" />
- Di Android Studio, buka file
build.gradle.kts
tingkat modul dan edit propertisecrets
. Jika propertisecrets
tidak ada, tambahkan.Edit properti plugin untuk menetapkanpropertiesFileName
kesecrets.properties
, menetapkandefaultPropertiesFileName
kelocal.defaults.properties
, dan menetapkan properti lainnya.secrets { // Optionally specify a different file name containing your secrets. // The plugin defaults to "local.properties" propertiesFileName = "secrets.properties" // A properties file containing default secret values. This file can be // checked in version control. defaultPropertiesFileName = "local.defaults.properties" }
5. Menambahkan Google Maps
Di bagian ini, Anda akan menambahkan Google Maps sehingga dimuat saat Anda meluncurkan aplikasi.
Menambahkan dependensi Maps Compose
Setelah kunci API Anda bisa diakses dalam aplikasi, langkah berikutnya adalah menambahkan dependensi Maps SDK for Android ke file build.gradle.kts
aplikasi Anda. Untuk membuat dengan Jetpack Compose, gunakan library Maps Compose yang menyediakan elemen Maps SDK for Android sebagai fungsi composable dan jenis data.
build.gradle.kts
Dalam file build.gradle.kts
tingkat aplikasi, ganti dependensi Maps SDK for Android non-compose:
dependencies {
// ...
// Google Maps SDK -- these are here for the data model. Remove these dependencies and replace
// with the compose versions.
implementation("com.google.android.gms:play-services-maps:18.2.0")
// KTX for the Maps SDK for Android library
implementation("com.google.maps.android:maps-ktx:5.0.0")
// KTX for the Maps SDK for Android Utility Library
implementation("com.google.maps.android:maps-utils-ktx:5.0.0")
}
dengan padanannya yang dapat dikomposisikan:
dependencies {
// ...
// Google Maps Compose library
val mapsComposeVersion = "4.4.1"
implementation("com.google.maps.android:maps-compose:$mapsComposeVersion")
// Google Maps Compose utility library
implementation("com.google.maps.android:maps-compose-utils:$mapsComposeVersion")
// Google Maps Compose widgets library
implementation("com.google.maps.android:maps-compose-widgets:$mapsComposeVersion")
}
Menambahkan composable Peta Google
Di MountainMap.kt
, tambahkan composable GoogleMap
di dalam composable Box
yang di-nest di dalam composable MapMountain
.
import com.google.maps.android.compose.GoogleMap
import com.google.maps.android.compose.GoogleMapComposable
// ...
@Composable
fun MountainMap(
paddingValues: PaddingValues,
viewState: MountainsScreenViewState.MountainList,
eventFlow: Flow<MountainsScreenEvent>,
selectedMarkerType: MarkerType,
) {
var isMapLoaded by remember { mutableStateOf(false) }
Box(
modifier = Modifier
.fillMaxSize()
.padding(paddingValues)
) {
// Add GoogleMap here
GoogleMap(
modifier = Modifier.fillMaxSize(),
onMapLoaded = { isMapLoaded = true }
)
// ...
}
}
Sekarang bangun dan jalankan aplikasi. Lihat! Anda akan melihat peta yang berpusat di Pulau Null yang terkenal, juga dikenal sebagai lintang nol dan bujur nol. Nanti, Anda akan mempelajari cara memosisikan peta ke lokasi dan tingkat zoom yang Anda inginkan, tetapi untuk saat ini, rayakan kemenangan pertama Anda.
6. Gaya visual peta berbasis cloud
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, saat membuat instance composable GoogleMap
, gunakan ID peta saat membuat objek GoogleMapOptions
yang ditetapkan ke parameter googleMapOptionsFactory
dalam konstruktor.
GoogleMap(
// ...
googleMapOptionsFactory = {
GoogleMapOptions().mapId("MyMapId")
}
)
Setelah menyelesaikannya, jalankan aplikasi untuk melihat peta dengan gaya yang Anda pilih.
7. Memuat data penanda
Tugas utama aplikasi ini adalah memuat kumpulan gunung dari penyimpanan lokal dan menampilkannya di GoogleMap
. Pada langkah ini, Anda akan menjelajahi infrastruktur yang disediakan untuk memuat data gunung dan menampilkannya ke UI.
Mountain
Class data Mountain
menyimpan semua data tentang setiap gunung.
data class Mountain(
val id: Int,
val name: String,
val location: LatLng,
val elevation: Meters,
)
Perhatikan bahwa pegunungan akan dipartisi berdasarkan ketinggiannya. Gunung yang memiliki ketinggian setidaknya 14.000 kaki disebut fourteener. Kode awal menyertakan fungsi ekstensi untuk melakukan pemeriksaan ini bagi Anda.
/**
* Extension function to determine whether a mountain is a "14er", i.e., has an elevation greater
* than 14,000 feet (~4267 meters).
*/
fun Mountain.is14er() = elevation >= 14_000.feet
MountainsScreenViewState
Class MountainsScreenViewState
menyimpan semua data yang diperlukan untuk merender tampilan. Status dapat berupa Loading
atau MountainList
, bergantung pada apakah daftar gunung telah selesai dimuat.
/**
* Sealed class representing the state of the mountain map view.
*/
sealed class MountainsScreenViewState {
data object Loading : MountainsScreenViewState()
data class MountainList(
// List of the mountains to display
val mountains: List<Mountain>,
// Bounding box that contains all of the mountains
val boundingBox: LatLngBounds,
// Switch indicating whether all the mountains or just the 14ers
val showingAllPeaks: Boolean = false,
) : MountainsScreenViewState()
}
Class yang disediakan: MountainsRepository
dan MountainsViewModel
Dalam project permulaan, class MountainsRepository
telah disediakan untuk Anda. Class ini membaca daftar tempat pegunungan yang disimpan dalam GPS Exchange Format
, atau file GPX, top_peaks.gpx
. Memanggil mountainsRepository.loadMountains()
akan menampilkan StateFlow<List<Mountain>>
.
MountainsRepository
class MountainsRepository(@ApplicationContext val context: Context) {
private val _mountains = MutableStateFlow(emptyList<Mountain>())
val mountains: StateFlow<List<Mountain>> = _mountains
private var loaded = false
/**
* Loads the list of mountains from the list of mountains from the raw resource.
*/
suspend fun loadMountains(): StateFlow<List<Mountain>> {
if (!loaded) {
loaded = true
_mountains.value = withContext(Dispatchers.IO) {
context.resources.openRawResource(R.raw.top_peaks).use { inputStream ->
readMountains(inputStream)
}
}
}
return mountains
}
/**
* Reads the [Waypoint]s from the given [inputStream] and returns a list of [Mountain]s.
*/
private fun readMountains(inputStream: InputStream) =
readWaypoints(inputStream).mapIndexed { index, waypoint ->
waypoint.toMountain(index)
}.toList()
// ...
}
MountainsViewModel
MountainsViewModel
adalah class ViewModel
yang memuat koleksi pegunungan dan mengekspos koleksi tersebut serta bagian lain dari status UI melalui mountainsScreenViewState
. mountainsScreenViewState
adalah hot StateFlow
yang dapat diamati UI sebagai status yang dapat diubah menggunakan fungsi ekstensi collectAsState
.
Dengan mengikuti prinsip arsitektur yang baik, MountainsViewModel
menyimpan semua status aplikasi. UI mengirim interaksi pengguna ke model tampilan menggunakan metode onEvent
.
@HiltViewModel
class MountainsViewModel
@Inject
constructor(
mountainsRepository: MountainsRepository
) : ViewModel() {
private val _eventChannel = Channel<MountainsScreenEvent>()
// Event channel to send events to the UI
internal fun getEventChannel() = _eventChannel.receiveAsFlow()
// Whether or not to show all of the high peaks
private var showAllMountains = MutableStateFlow(false)
val mountainsScreenViewState =
mountainsRepository.mountains.combine(showAllMountains) { allMountains, showAllMountains ->
if (allMountains.isEmpty()) {
MountainsScreenViewState.Loading
} else {
val filteredMountains =
if (showAllMountains) allMountains else allMountains.filter { it.is14er() }
val boundingBox = filteredMountains.map { it.location }.toLatLngBounds()
MountainsScreenViewState.MountainList(
mountains = filteredMountains,
boundingBox = boundingBox,
showingAllPeaks = showAllMountains,
)
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5000),
initialValue = MountainsScreenViewState.Loading
)
init {
// Load the full set of mountains
viewModelScope.launch {
mountainsRepository.loadMountains()
}
}
// Handle user events
fun onEvent(event: MountainsViewModelEvent) {
when (event) {
OnZoomAll -> onZoomAll()
OnToggleAllPeaks -> toggleAllPeaks()
}
}
private fun onZoomAll() {
sendScreenEvent(MountainsScreenEvent.OnZoomAll)
}
private fun toggleAllPeaks() {
showAllMountains.value = !showAllMountains.value
}
// Send events back to the UI via the event channel
private fun sendScreenEvent(event: MountainsScreenEvent) {
viewModelScope.launch { _eventChannel.send(event) }
}
}
Jika penasaran dengan penerapan class ini, Anda dapat mengaksesnya di GitHub atau membuka class MountainsRepository
dan MountainsViewModel
di Android Studio.
Menggunakan ViewModel
Model tampilan digunakan di MainActivity
untuk mendapatkan viewState
. Anda akan menggunakan viewState
untuk merender penanda nanti dalam codelab ini. Perhatikan bahwa kode ini sudah disertakan dalam project awal dan ditampilkan di sini hanya sebagai referensi.
val viewModel: MountainsViewModel by viewModels()
val screenViewState = viewModel.mountainsScreenViewState.collectAsState()
val viewState = screenViewState.value
8. Posisikan kamera
Default GoogleMap
berpusat pada lintang nol, bujur nol. Penanda yang akan Anda render berada di Negara Bagian Colorado di Amerika Serikat. viewState
yang disediakan oleh model tampilan menampilkan LatLngBounds yang berisi semua penanda.
Di MountainMap.kt
, buat CameraPositionState
yang diinisialisasi ke tengah kotak pembatas. Tetapkan parameter cameraPositionState
dari GoogleMap
ke variabel cameraPositionState
yang baru saja Anda buat.
fun MountainMap(
// ...
) {
// ...
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(viewState.boundingBox.center, 5f)
}
GoogleMap(
// ...
cameraPositionState = cameraPositionState,
)
}
Sekarang jalankan kode dan lihat peta yang berpusat di Colorado.
Melakukan zoom ke cakupan penanda
Untuk benar-benar memfokuskan peta pada penanda, tambahkan fungsi zoomAll
ke akhir file MountainMap.kt
. Perhatikan bahwa fungsi ini memerlukan CoroutineScope
karena menganimasikan kamera ke lokasi baru adalah operasi asinkron yang memerlukan waktu untuk diselesaikan.
fun zoomAll(
scope: CoroutineScope,
cameraPositionState: CameraPositionState,
boundingBox: LatLngBounds
) {
scope.launch {
cameraPositionState.animate(
update = CameraUpdateFactory.newLatLngBounds(boundingBox, 64),
durationMs = 1000
)
}
}
Selanjutnya, tambahkan kode untuk memanggil fungsi zoomAll
setiap kali batas di sekitar kumpulan penanda berubah atau saat pengguna mengklik tombol rentang zoom di panel TopApp. Perhatikan bahwa tombol rentang zoom sudah terhubung untuk mengirim peristiwa ke model tampilan. Anda hanya perlu mengumpulkan peristiwa tersebut dari model tampilan dan memanggil fungsi zoomAll
sebagai respons.
fun MountainMap(
// ...
) {
// ...
val scope = rememberCoroutineScope()
LaunchedEffect(key1 = viewState.boundingBox) {
zoomAll(scope, cameraPositionState, viewState.boundingBox)
}
LaunchedEffect(true) {
eventFlow.collect { event ->
when (event) {
MountainsScreenEvent.OnZoomAll -> {
zoomAll(scope, cameraPositionState, viewState.boundingBox)
}
}
}
}
}
Sekarang, saat Anda menjalankan aplikasi, peta akan dimulai dengan fokus pada area tempat penanda akan ditempatkan. Anda dapat memosisikan ulang dan mengubah zoom, serta mengklik tombol batas zoom akan memfokuskan kembali peta di sekitar area penanda. Itu adalah kemajuan! Namun, peta harus memiliki sesuatu untuk dilihat. Dan itulah yang akan Anda lakukan di langkah berikutnya.
9. Penanda dasar
Pada langkah ini, Anda menambahkan Penanda ke peta yang mewakili lokasi menarik yang ingin Anda sorot pada peta. Anda akan menggunakan daftar gunung yang telah disediakan dalam project permulaan dan menambahkan tempat ini sebagai penanda di peta.
Mulai dengan menambahkan blok konten ke GoogleMap
. Akan ada beberapa jenis penanda, jadi tambahkan pernyataan when
untuk membuat cabang ke setiap jenis dan Anda akan menerapkannya satu per satu dalam langkah-langkah berikutnya.
GoogleMap(
// ...
) {
when (selectedMarkerType) {
MarkerType.Basic -> {
BasicMarkersMapContent(
mountains = viewState.mountains,
)
}
MarkerType.Advanced -> {
AdvancedMarkersMapContent(
mountains = viewState.mountains,
)
}
MarkerType.Clustered -> {
ClusteringMarkersMapContent(
mountains = viewState.mountains,
)
}
}
}
Menambahkan penanda
Anotasikan BasicMarkersMapContent
dengan @GoogleMapComposable
. Perhatikan bahwa Anda hanya dapat menggunakan fungsi @GoogleMapComposable
di blok konten GoogleMap
. Objek mountains
memiliki daftar objek Mountain
. Anda akan menambahkan penanda untuk setiap gunung dalam daftar tersebut, menggunakan lokasi, nama, dan ketinggian dari objek Mountain
. Lokasi digunakan untuk menetapkan parameter status Marker
yang, pada gilirannya, mengontrol posisi penanda.
// ...
import com.google.android.gms.maps.model.Marker
import com.google.maps.android.compose.GoogleMapComposable
import com.google.maps.android.compose.Marker
import com.google.maps.android.compose.rememberMarkerState
@Composable
@GoogleMapComposable
fun BasicMarkersMapContent(
mountains: List<Mountain>,
onMountainClick: (Marker) -> Boolean = { false }
) {
mountains.forEach { mountain ->
Marker(
state = rememberMarkerState(position = mountain.location),
title = mountain.name,
snippet = mountain.elevation.toElevationString(),
tag = mountain,
onClick = { marker ->
onMountainClick(marker)
false
},
zIndex = if (mountain.is14er()) 5f else 2f
)
}
}
Lanjutkan dan jalankan aplikasi, Anda akan melihat penanda yang baru saja Anda tambahkan.
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.
Project permulaan mencakup fungsi helper, vectorToBitmap
, untuk membuat BitmapDescriptor
dari @DrawableResource
.
Kode awal menyertakan ikon gunung, baseline_filter_hdr_24.xml
, yang akan Anda gunakan untuk menyesuaikan penanda.
Fungsi vectorToBitmap
mengonversi vektor drawable menjadi BitmapDescriptor
untuk digunakan dengan library Maps. Warna ikon ditetapkan menggunakan instance BitmapParameters
.
data class BitmapParameters(
@DrawableRes val id: Int,
@ColorInt val iconColor: Int,
@ColorInt val backgroundColor: Int? = null,
val backgroundAlpha: Int = 168,
val padding: Int = 16,
)
fun vectorToBitmap(context: Context, parameters: BitmapParameters): BitmapDescriptor {
// ...
}
Gunakan fungsi vectorToBitmap
untuk membuat dua BitmapDescriptor
yang disesuaikan; satu untuk gunung dengan ketinggian lebih dari 4.267 meter dan satu untuk gunung biasa. Kemudian, gunakan parameter icon
dari composable Marker
untuk menyetel ikon. Selain itu, tetapkan parameter anchor
untuk mengubah lokasi penanda relatif terhadap ikon. Menggunakan bagian tengah akan lebih baik untuk ikon melingkar ini.
@Composable
@GoogleMapComposable
fun BasicMarkersMapContent(
// ...
) {
// Create mountainIcon and fourteenerIcon
val mountainIcon = vectorToBitmap(
LocalContext.current,
BitmapParameters(
id = R.drawable.baseline_filter_hdr_24,
iconColor = MaterialTheme.colorScheme.secondary.toArgb(),
backgroundColor = MaterialTheme.colorScheme.secondaryContainer.toArgb(),
)
)
val fourteenerIcon = vectorToBitmap(
LocalContext.current,
BitmapParameters(
id = R.drawable.baseline_filter_hdr_24,
iconColor = MaterialTheme.colorScheme.onPrimary.toArgb(),
backgroundColor = MaterialTheme.colorScheme.primary.toArgb(),
)
)
mountains.forEach { mountain ->
val icon = if (mountain.is14er()) fourteenerIcon else mountainIcon
Marker(
// ...
anchor = Offset(0.5f, 0.5f),
icon = icon,
)
}
}
Jalankan aplikasi dan kagumi penanda yang disesuaikan. Aktifkan tombol Show all
untuk melihat rangkaian pegunungan lengkap. Pegunungan akan memiliki penanda yang berbeda, bergantung pada apakah pegunungan tersebut merupakan pegunungan empat belas ribu kaki.
10. Penanda lanjutan
AdvancedMarker
menambahkan fitur ekstra ke Markers
dasar. Pada langkah ini, Anda akan menetapkan perilaku tabrakan dan mengonfigurasi gaya pin.
Tambahkan @GoogleMapComposable
ke fungsi AdvancedMarkersMapContent
. Lakukan pengulangan pada mountains
dengan menambahkan AdvancedMarker
untuk setiap mountains
.
@Composable
@GoogleMapComposable
fun AdvancedMarkersMapContent(
mountains: List<Mountain>,
onMountainClick: (Marker) -> Boolean = { false },
) {
mountains.forEach { mountain ->
AdvancedMarker(
state = rememberMarkerState(position = mountain.location),
title = mountain.name,
snippet = mountain.elevation.toElevationString(),
collisionBehavior = AdvancedMarkerOptions.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL,
onClick = { marker ->
onMountainClick(marker)
false
}
)
}
}
Perhatikan parameter collisionBehavior
. Dengan menyetel parameter ini ke REQUIRED_AND_HIDES_OPTIONAL
, penanda Anda akan menggantikan penanda dengan prioritas yang lebih rendah. Anda dapat melihatnya dengan memperbesar penanda dasar dibandingkan dengan penanda lanjutan. Penanda dasar kemungkinan akan memiliki penanda Anda dan penanda yang ditempatkan di lokasi yang sama dalam peta dasar. Penanda lanjutan akan menyebabkan penanda berprioritas lebih rendah disembunyikan.
Jalankan aplikasi untuk melihat Penanda lanjutan. Pastikan untuk memilih tab Advanced markers
di baris navigasi bawah.
AdvancedMarkers
yang disesuaikan
Ikon menggunakan skema warna primer dan sekunder untuk membedakan antara gunung berketinggian lebih dari 4.267 meter dan gunung lainnya. Gunakan fungsi vectorToBitmap
untuk membuat dua BitmapDescriptor
; satu untuk gunung berketinggian lebih dari 4.000 meter dan satu untuk gunung lainnya. Gunakan ikon tersebut untuk membuat pinConfig
kustom untuk setiap jenis. Terakhir, terapkan pin ke AdvancedMarker
yang sesuai berdasarkan fungsi is14er()
.
@Composable
@GoogleMapComposable
fun AdvancedMarkersMapContent(
mountains: List<Mountain>,
onMountainClick: (Marker) -> Boolean = { false },
) {
val mountainIcon = vectorToBitmap(
LocalContext.current,
BitmapParameters(
id = R.drawable.baseline_filter_hdr_24,
iconColor = MaterialTheme.colorScheme.onSecondary.toArgb(),
)
)
val mountainPin = with(PinConfig.builder()) {
setGlyph(PinConfig.Glyph(mountainIcon))
setBackgroundColor(MaterialTheme.colorScheme.secondary.toArgb())
setBorderColor(MaterialTheme.colorScheme.onSecondary.toArgb())
build()
}
val fourteenerIcon = vectorToBitmap(
LocalContext.current,
BitmapParameters(
id = R.drawable.baseline_filter_hdr_24,
iconColor = MaterialTheme.colorScheme.onPrimary.toArgb(),
)
)
val fourteenerPin = with(PinConfig.builder()) {
setGlyph(PinConfig.Glyph(fourteenerIcon))
setBackgroundColor(MaterialTheme.colorScheme.primary.toArgb())
setBorderColor(MaterialTheme.colorScheme.onPrimary.toArgb())
build()
}
mountains.forEach { mountain ->
val pin = if (mountain.is14er()) fourteenerPin else mountainPin
AdvancedMarker(
state = rememberMarkerState(position = mountain.location),
title = mountain.name,
snippet = mountain.elevation.toElevationString(),
collisionBehavior = AdvancedMarkerOptions.CollisionBehavior.REQUIRED_AND_HIDES_OPTIONAL,
pinConfig = pin,
onClick = { marker ->
onMountainClick(marker)
false
}
)
}
}
11. Marker berkelompok
Pada langkah ini, Anda akan menggunakan composable Clustering
untuk menambahkan pengelompokan item berbasis zoom.
Composable Clustering
memerlukan kumpulan ClusterItem
. MountainClusterItem
mengimplementasikan antarmuka ClusterItem
. Tambahkan class ini ke file ClusteringMarkersMapContent.kt
.
data class MountainClusterItem(
val mountain: Mountain,
val snippetString: String
) : ClusterItem {
override fun getPosition() = mountain.location
override fun getTitle() = mountain.name
override fun getSnippet() = snippetString
override fun getZIndex() = 0f
}
Sekarang tambahkan kode untuk membuat MountainClusterItem
dari daftar gunung. Perhatikan bahwa kode ini menggunakan UnitsConverter
untuk mengonversi ke unit tampilan yang sesuai untuk pengguna berdasarkan lokalitasnya. Hal ini disiapkan di MainActivity
menggunakan CompositionLocal
@OptIn(MapsComposeExperimentalApi::class)
@Composable
@GoogleMapComposable
fun ClusteringMarkersMapContent(
mountains: List<Mountain>,
// ...
) {
val unitsConverter = LocalUnitsConverter.current
val resources = LocalContext.current.resources
val mountainClusterItems by remember(mountains) {
mutableStateOf(
mountains.map { mountain ->
MountainClusterItem(
mountain = mountain,
snippetString = unitsConverter.toElevationString(resources, mountain.elevation)
)
}
)
}
Clustering(
items = mountainClusterItems,
)
}
Dengan kode tersebut, penanda dikelompokkan berdasarkan tingkat zoom. Bagus dan rapi!
Menyesuaikan cluster
Seperti jenis penanda lainnya, penanda yang dikelompokkan dapat disesuaikan. Parameter clusterItemContent
dari composable Clustering
menetapkan blok composable kustom untuk merender item yang tidak dikelompokkan. Terapkan fungsi @Composable
untuk membuat penanda. Fungsi SingleMountain
merender composable Icon
Material 3 dengan skema warna latar belakang yang disesuaikan.
Di ClusteringMarkersMapContent.kt
, buat class data yang menentukan skema warna untuk penanda:
data class IconColor(val iconColor: Color, val backgroundColor: Color, val borderColor: Color)
Selain itu, di ClusteringMarkersMapContent.kt
, buat fungsi composable untuk merender ikon untuk skema warna tertentu:
@Composable
private fun SingleMountain(
colors: IconColor,
) {
Icon(
painterResource(id = R.drawable.baseline_filter_hdr_24),
tint = colors.iconColor,
contentDescription = "",
modifier = Modifier
.size(32.dp)
.padding(1.dp)
.drawBehind {
drawCircle(color = colors.backgroundColor, style = Fill)
drawCircle(color = colors.borderColor, style = Stroke(width = 3f))
}
.padding(4.dp)
)
}
Sekarang buat skema warna untuk gunung berketinggian lebih dari 4.000 meter dan skema warna lain untuk gunung lainnya. Di blok clusterItemContent
, pilih skema warna berdasarkan apakah gunung yang diberikan merupakan gunung dengan ketinggian lebih dari 14.000 kaki atau tidak.
fun ClusteringMarkersMapContent(
mountains: List<Mountain>,
// ...
) {
// ...
val backgroundAlpha = 0.6f
val fourteenerColors = IconColor(
iconColor = MaterialTheme.colorScheme.onPrimary,
backgroundColor = MaterialTheme.colorScheme.primary.copy(alpha = backgroundAlpha),
borderColor = MaterialTheme.colorScheme.primary
)
val otherColors = IconColor(
iconColor = MaterialTheme.colorScheme.secondary,
backgroundColor = MaterialTheme.colorScheme.secondaryContainer.copy(alpha = backgroundAlpha),
borderColor = MaterialTheme.colorScheme.secondary
)
// ...
Clustering(
items = mountainClusterItems,
clusterItemContent = { mountainItem ->
val colors = if (mountainItem.mountain.is14er()) {
fourteenerColors
} else {
otherColors
}
SingleMountain(colors)
},
)
}
Sekarang, jalankan aplikasi untuk melihat versi item individual yang disesuaikan.
12. 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 Polygon
untuk menampilkannya pada peta. Atau, jika Anda ingin memperbaiki gambar pada permukaan bumi, Anda dapat menggunakan GroundOverlay
.
Dalam tugas ini, Anda akan mempelajari cara menggambar bentuk, khususnya garis luar di sekitar Negara Bagian Colorado. Batas Colorado ditentukan antara 37°LU dan 41°LU serta 102°03'BB dan 109°03'BB. Hal ini membuat penggambaran garis luar menjadi cukup mudah.
Kode awal menyertakan class DMS
untuk mengonversi dari notasi derajat-menit-detik ke derajat desimal.
enum class Direction(val sign: Int) {
NORTH(1),
EAST(1),
SOUTH(-1),
WEST(-1)
}
/**
* Degrees, minutes, seconds utility class
*/
data class DMS(
val direction: Direction,
val degrees: Double,
val minutes: Double = 0.0,
val seconds: Double = 0.0,
)
fun DMS.toDecimalDegrees(): Double =
(degrees + (minutes / 60) + (seconds / 3600)) * direction.sign
Dengan class DMS, Anda dapat menggambar batas Colorado dengan menentukan empat lokasi sudut LatLng
dan merendernya sebagai Polygon
. Tambahkan kode berikut ke MountainMap.kt
@Composable
@GoogleMapComposable
fun ColoradoPolygon() {
val north = 41.0
val south = 37.0
val east = DMS(WEST, 102.0, 3.0).toDecimalDegrees()
val west = DMS(WEST, 109.0, 3.0).toDecimalDegrees()
val locations = listOf(
LatLng(north, east),
LatLng(south, east),
LatLng(south, west),
LatLng(north, west),
)
Polygon(
points = locations,
strokeColor = MaterialTheme.colorScheme.tertiary,
strokeWidth = 3F,
fillColor = MaterialTheme.colorScheme.tertiaryContainer.copy(alpha = 0.3f),
)
}
Sekarang panggil ColoradoPolyon()
di dalam blok konten GoogleMap
.
@Composable
fun MountainMap(
// ...
) {
Box(
// ...
) {
GoogleMap(
// ...
) {
ColoradoPolygon()
}
}
}
Sekarang aplikasi menguraikan Negara Bagian Colorado sambil memberikan pengisian yang halus.
13. Menambahkan lapisan KML dan skala batang
Di bagian terakhir ini, Anda akan menguraikan secara kasar berbagai pegunungan dan menambahkan skala batang ke peta.
Gambarkan pegunungan
Sebelumnya, Anda telah menggambar garis batas di sekitar Colorado. Di sini, Anda akan menambahkan bentuk yang lebih kompleks ke peta. Kode awal mencakup file Keyhole Markup Language, atau KML, yang secara kasar menguraikan pegunungan penting. Library Utilitas Maps SDK for Android memiliki fungsi untuk menambahkan lapisan KML ke peta. Di MountainMap.kt
, tambahkan panggilan MapEffect
di blok konten GoogleMap
setelah blok when
. Fungsi MapEffect
dipanggil dengan objek GoogleMap
. Composable ini dapat berfungsi sebagai jembatan yang berguna antara API dan library non-composable yang memerlukan objek GoogleMap
.
fun MountainMap(
// ...
) {
var isMapLoaded by remember { mutableStateOf(false) }
val context = LocalContext.current
GoogleMap(
// ...
) {
// ...
when (selectedMarkerType) {
// ...
}
// This code belongs inside the GoogleMap content block, but outside of
// the 'when' statement
MapEffect(key1 = true) {map ->
val layer = KmlLayer(map, R.raw.mountain_ranges, context)
layer.addLayerToMap()
}
}
Menambahkan skala peta
Sebagai tugas terakhir, Anda akan menambahkan skala ke peta. ScaleBar
menerapkan composable skala yang dapat ditambahkan ke peta. Perhatikan bahwa ScaleBar
bukan
@GoogleMapComposable
dan oleh karena itu tidak dapat ditambahkan ke konten GoogleMap
. Sebagai gantinya, Anda menambahkannya ke Box
yang memuat peta.
Box(
// ...
) {
GoogleMap(
// ...
) {
// ...
}
ScaleBar(
modifier = Modifier
.padding(top = 5.dp, end = 15.dp)
.align(Alignment.TopEnd),
cameraPositionState = cameraPositionState
)
// ...
}
Jalankan aplikasi untuk melihat codelab yang telah diimplementasikan sepenuhnya.
14. Mendapatkan kode solusi
Untuk mendownload kode codelab yang sudah selesai, Anda dapat menggunakan perintah berikut:
- Lakukan clone repositori jika sudah menginstal
git
.
$ git clone https://github.com/googlemaps-samples/codelab-maps-platform-101-compose.git
Atau, Anda dapat mengklik tombol berikut untuk mendownload kode sumber.
- Setelah mendapatkan kode, lanjutkan dan buka project yang ada dalam direktori
solution
di Android Studio.
15. 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
- Maps SDK for Android - Buat peta, lokasi, dan pengalaman geospasial yang dinamis, interaktif, dan disesuaikan untuk aplikasi Android Anda.
- Library Maps Compose - sekumpulan fungsi composable dan jenis data open source yang dapat Anda gunakan dengan Jetpack Compose untuk membuat aplikasi.
- android-maps-compose - 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