1. Başlamadan Önce
Bu codelab'de, çeşitli işaret türlerini kullanarak ABD'nin Colorado eyaletindeki dağların haritasını gösteren bir uygulama oluşturarak Android için Haritalar SDK'sını uygulamanıza nasıl entegre edeceğinizi ve temel özelliklerini nasıl kullanacağınızı öğreneceksiniz. Ayrıca, haritada başka şekiller çizmeyi de öğreneceksiniz.
Codelab'i tamamladığınızda aşağıdaki gibi görünür:
Ön koşullar
- Kotlin, Jetpack Compose ve Android geliştirme hakkında temel bilgiler
Yapacaklarınız
- Android uygulamasına
GoogleMap
eklemek için Android için Haritalar SDK'sında Haritalar Compose kitaplığını etkinleştirme ve kullanma - İşaretçi ekleme ve özelleştirme
- Haritada poligon çizme
- Kameranın bakış açısını programatik olarak kontrol etme
İhtiyacınız olanlar
- Android için Haritalar SDK'sı
- Faturalandırmanın etkinleştirildiği bir Google Hesabı
- Android Studio'nun en son kararlı sürümü
- Android 5.0 veya sonraki sürümlere dayalı Google API'leri platformunu çalıştıran bir Android cihaz ya da Android emülatörü (yükleme adımları için Android emülatöründe uygulamaları çalıştırma başlıklı makaleyi inceleyin)
- İnternet bağlantısı
2. Hazırlanın
Etkinleştirme işleminin bir sonraki adımında Android için Haritalar SDK'sı'nı etkinleştirmeniz gerekir.
Google Haritalar Platformu'nu ayarlama
Henüz bir Google Cloud Platform hesabınız ve faturalandırmanın etkinleştirildiği bir projeniz yoksa lütfen faturalandırma hesabı ve proje oluşturmak için Google Haritalar Platformu'nu Kullanmaya Başlama kılavuzuna bakın.
- Cloud Console'da proje açılır menüsünü tıklayın ve bu codelab için kullanmak istediğiniz projeyi seçin.
- Bu codelab için gereken Google Haritalar Platformu API'lerini ve SDK'larını Google Cloud Marketplace'te etkinleştirin. Bunun için bu videodaki veya bu dokümandaki adımları uygulayın.
- Cloud Console'un Kimlik Bilgileri sayfasında bir API anahtarı oluşturun. Bu videodaki veya bu dokümandaki adımları uygulayabilirsiniz. Google Haritalar Platformu'na yapılan tüm istekler için API anahtarı gerekir.
3. Hızlı başlangıç
Mümkün olduğunca hızlı bir şekilde başlamanıza yardımcı olmak için bu codelab'i takip etmenize yardımcı olacak başlangıç kodunu aşağıda bulabilirsiniz. Çözüme geçebilirsiniz ancak kendiniz oluşturmak için tüm adımları takip etmek istiyorsanız okumaya devam edin.
git
yüklüyse depoyu klonlayın.
git clone https://github.com/googlemaps-samples/codelab-maps-platform-101-compose.git
Alternatif olarak, kaynak kodunu indirmek için aşağıdaki düğmeyi tıklayabilirsiniz.
- Kodu aldıktan sonra Android Studio'da
starter
dizinindeki projeyi açın.
4. API anahtarınızı projeye ekleme
Bu bölümde, API anahtarınızı uygulamanız tarafından güvenli bir şekilde referans verilebilecek şekilde nasıl saklayacağınız açıklanmaktadır. API anahtarınızı sürüm kontrol sisteminize işlememeniz gerekir. Bu nedenle, projenizin kök dizininin yerel kopyasına yerleştirilecek olan secrets.properties
dosyasında saklamanızı öneririz. secrets.properties
dosyası hakkında daha fazla bilgi için Gradle özellik dosyaları konusuna bakın.
Bu görevi kolaylaştırmak için Android İçin Secrets Gradle Plugin'i kullanmanızı öneririz.
Google Haritalar projenize Android İçin Secrets Gradle Plugin'i yüklemek üzere:
- Android Studio'da üst düzey
build.gradle.kts
dosyanızı açın vebuildscript
altındakidependencies
öğesine aşağıdaki kodu ekleyin.buildscript { dependencies { classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1") } }
- Modül düzeyindeki
build.gradle.kts
dosyanızı açın ve aşağıdaki koduplugins
öğesine ekleyin.plugins { // ... id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin") }
- Modül düzeyindeki
build.gradle.kts
dosyanızdatargetSdk
vecompileSdk
değerlerinin en az 34 olarak ayarlandığından emin olun. - Dosyayı kaydedin ve projenizi Gradle ile senkronize edin.
secrets.properties
dosyasını en üst düzey dizininizde açın ve aşağıdaki kodu ekleyin.YOUR_API_KEY
kısmını API anahtarınızla değiştirin.secrets.properties
, sürüm kontrol sistemine kaydedilmekten hariç tutulduğu için anahtarınızı bu dosyada saklayın.MAPS_API_KEY=YOUR_API_KEY
- Dosyayı kaydedin.
local.defaults.properties
dosyasını üst düzey dizininizde (secrets.properties
dosyasıyla aynı klasörde) oluşturun ve aşağıdaki kodu ekleyin. Bu dosyanın amacı,MAPS_API_KEY=DEFAULT_API_KEY
secrets.properties
dosyası bulunamazsa API anahtarı için bir yedek konum sağlamaktır. Böylece derlemeler başarısız olmaz. Bu durum, uygulamayı bir sürüm kontrol sisteminden klonladığınızda ve API anahtarınızı sağlamak için henüz yerel olarak birsecrets.properties
dosyası oluşturmadığınızda meydana gelir.- Dosyayı kaydedin.
AndroidManifest.xml
dosyanızdacom.google.android.geo.API_KEY
bölümüne gidin veandroid:value
özelliğini güncelleyin.<meta-data>
etiketi yoksa<meta-data>
etiketinin alt öğesi olarak oluşturun.<application>
<meta-data android:name="com.google.android.geo.API_KEY" android:value="${MAPS_API_KEY}" />
- Android Studio'da modül düzeyindeki
build.gradle.kts
dosyanızı açın vesecrets
özelliğini düzenleyin.secrets
özelliği yoksa ekleyin.Eklentinin özelliklerini düzenleyerekpropertiesFileName
özelliğinisecrets.properties
,defaultPropertiesFileName
özelliğinilocal.defaults.properties
ve diğer özellikleri ayarlayın.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. Google Haritalar'ı ekleme
Bu bölümde, uygulamayı başlattığınızda yüklenecek bir Google Haritası ekleyeceksiniz.
Maps Compose bağımlılıklarını ekleme
API anahtarınıza artık uygulama içinden erişilebildiğine göre, bir sonraki adım Android için Haritalar SDK'sı bağımlılığını uygulamanızın build.gradle.kts
dosyasına eklemektir. Jetpack Compose ile geliştirme yapmak için Android için Haritalar SDK'sının öğelerini composable işlevler ve veri türleri olarak sağlayan Maps Compose kitaplığını kullanın.
build.gradle.kts
Uygulama düzeyindeki build.gradle.kts
dosyasında, Compose olmayan Android için Haritalar SDK'sı bağımlılıklarını aşağıdakiyle değiştirin:
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")
}
ile birleştirilebilir:
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")
}
Google Haritası composable'ı ekleme
MountainMap.kt
içinde, MapMountain
composable'ın içine yerleştirilmiş Box
composable'ın içine GoogleMap
composable'ı ekleyin.
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 }
)
// ...
}
}
Şimdi uygulamayı derleyip çalıştırın. İşte karşınızda! Enlem sıfır ve boylam sıfır olarak da bilinen kötü şöhretli Null Adası'nın ortasında bir harita görmeniz gerekir. Daha sonra haritayı istediğiniz konuma ve yakınlaştırma düzeyine nasıl yerleştireceğinizi öğreneceksiniz. Ancak şimdilik ilk başarınızı kutlayın.
6. Bulut tabanlı harita stili düzenleme
Bulut tabanlı harita stilleri'ni kullanarak haritanızın stilini özelleştirebilirsiniz.
Harita kimliği oluşturma
Henüz ilişkili bir harita stiline sahip bir harita kimliği oluşturmadıysanız aşağıdaki adımları tamamlamak için Harita Kimlikleri kılavuzuna bakın:
- Harita kimliği oluşturun.
- Harita kimliğini harita stiliyle ilişkilendirin.
Harita kimliğini uygulamanıza ekleme
Oluşturduğunuz harita kimliğini kullanmak için GoogleMap
composable'ınızı oluştururken yapıcıda googleMapOptionsFactory
parametresine atanan bir GoogleMapOptions
nesnesi oluştururken harita kimliğini kullanın.
GoogleMap(
// ...
googleMapOptionsFactory = {
GoogleMapOptions().mapId("MyMapId")
}
)
Bu işlemi tamamladıktan sonra, seçtiğiniz stildeki haritanızı görmek için uygulamayı çalıştırın.
7. İşaretçi verilerini yükleme
Uygulamanın temel görevi, yerel depolama alanından bir dağ koleksiyonu yüklemek ve bu dağları GoogleMap
içinde göstermektir. Bu adımda, dağ verilerini yüklemek ve kullanıcı arayüzünde göstermek için sağlanan altyapıyı keşfedeceksiniz.
Mountain
Mountain
veri sınıfı, her dağla ilgili tüm verileri içerir.
data class Mountain(
val id: Int,
val name: String,
val location: LatLng,
val elevation: Meters,
)
Dağların daha sonra yüksekliğe göre bölümlere ayrılacağını unutmayın. En az 4.267 metre yüksekliğindeki dağlara dört binlikler denir. Başlangıç kodu, bu kontrolü sizin için yapacak bir uzantı işlevi içerir.
/**
* 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
MountainsScreenViewState
sınıfı, görünümü oluşturmak için gereken tüm verileri içerir. Dağ listesinin yüklenmesi tamamlanıp tamamlanmamasına bağlı olarak Loading
veya MountainList
durumunda olabilir.
/**
* 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()
}
Sunulan sınıflar: MountainsRepository
ve MountainsViewModel
Başlangıç projesinde MountainsRepository
sınıfı sizin için sağlanmıştır. Bu sınıf, GPS Exchange Format
veya GPX dosyasında depolanan dağlık yerlerin listesini okur top_peaks.gpx
. mountainsRepository.loadMountains()
aranırken StateFlow<List<Mountain>>
döndürülür.
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
, dağ koleksiyonlarını yükleyen ve bu koleksiyonların yanı sıra kullanıcı arayüzü durumunun diğer bölümlerini mountainsScreenViewState
aracılığıyla gösteren bir ViewModel
sınıfıdır. mountainsScreenViewState
, collectAsState
uzantı işlevi kullanılarak kullanıcı arayüzünün değiştirilebilir durum olarak gözlemleyebileceği bir akış StateFlow
'dır.
MountainsViewModel
, sağlam mimari ilkeler doğrultusunda uygulamanın tüm durumunu tutar. Kullanıcı arayüzü, onEvent
yöntemini kullanarak kullanıcı etkileşimlerini görünüm modeline gönderir.
@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) }
}
}
Bu sınıfların uygulanması hakkında bilgi edinmek isterseniz GitHub'da bunlara erişebilir veya Android Studio'da MountainsRepository
ve MountainsViewModel
sınıflarını açabilirsiniz.
ViewModel'i kullanma
Görünüm modeli, MainActivity
içinde viewState
değerini almak için kullanılır. Bu işaretçileri oluşturmak için viewState
simgesini bu codelab'in ilerleyen bölümlerinde kullanacaksınız. Bu kodun başlangıç projesine zaten dahil edildiğini ve burada yalnızca referans amacıyla gösterildiğini unutmayın.
val viewModel: MountainsViewModel by viewModels()
val screenViewState = viewModel.mountainsScreenViewState.collectAsState()
val viewState = screenViewState.value
8. Kamerayı konumlandırma
GoogleMap
varsayılan olarak enlem sıfır, boylam sıfır olarak ortalanır. Oluşturacağınız işaretçiler ABD'nin Colorado eyaletinde bulunuyor. Görünüm modeli tarafından sağlanan viewState
, tüm işaretçileri içeren bir LatLngBounds sunar.
MountainMap.kt
, sınırlayıcı kutunun merkezine başlatılmış bir CameraPositionState
oluşturun. GoogleMap
öğesinin cameraPositionState
parametresini, yeni oluşturduğunuz cameraPositionState
değişkeni olarak ayarlayın.
fun MountainMap(
// ...
) {
// ...
val cameraPositionState = rememberCameraPositionState {
position = CameraPosition.fromLatLngZoom(viewState.boundingBox.center, 5f)
}
GoogleMap(
// ...
cameraPositionState = cameraPositionState,
)
}
Şimdi kodu çalıştırın ve haritanın Colorado'da ortalandığını görün.
İşaretçi kapsamına yakınlaştırma
Haritayı işaretçilere gerçekten odaklamak için zoomAll
işlevini MountainMap.kt
dosyasının sonuna ekleyin. Bu işlevin CoroutineScope
gerektirdiğini unutmayın. Bunun nedeni, kameranın yeni bir konuma animasyonunun tamamlanması zaman alan eşzamansız bir işlem olmasıdır.
fun zoomAll(
scope: CoroutineScope,
cameraPositionState: CameraPositionState,
boundingBox: LatLngBounds
) {
scope.launch {
cameraPositionState.animate(
update = CameraUpdateFactory.newLatLngBounds(boundingBox, 64),
durationMs = 1000
)
}
}
Ardından, işaretçi koleksiyonunun etrafındaki sınırlar her değiştiğinde veya kullanıcı TopApp çubuğundaki yakınlaştırma düğmesini tıkladığında zoomAll
işlevini çağıran kodu ekleyin. Tümünü göster düğmesinin, etkinlikleri görünüm modeline gönderecek şekilde zaten bağlandığını unutmayın. Bu etkinlikleri yalnızca görünüm modelinden toplamanız ve yanıt olarak zoomAll
işlevini çağırmanız gerekir.
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)
}
}
}
}
}
Artık uygulamayı çalıştırdığınızda harita, işaretçilerin yerleştirileceği alana odaklanarak başlar. Yeniden konumlandırabilir ve yakınlaştırmayı değiştirebilirsiniz. Yakınlaştırma sınırları düğmesini tıkladığınızda harita, işaretçi alanına odaklanacak şekilde yeniden ayarlanır. İlerleme kaydediyorsunuz. Ancak haritada gerçekten bakılacak bir şey olmalıdır. Bir sonraki adımda bu işlemi yapacaksınız.
9. Temel işaretçiler
Bu adımda, haritada vurgulamak istediğiniz ilgi çekici yerleri temsil eden işaretçiler ekleyeceksiniz. Başlangıç projesinde sağlanan dağ listesini kullanacak ve bu yerleri haritaya işaretçi olarak ekleyeceksiniz.
Başlamak için GoogleMap
bölümüne bir içerik bloğu ekleyin. Birden fazla işaretçi türü olacaktır. Bu nedenle, her türe dallanmak için bir when
ifadesi ekleyin. Sonraki adımlarda her birini sırayla uygulayacaksınız.
GoogleMap(
// ...
) {
when (selectedMarkerType) {
MarkerType.Basic -> {
BasicMarkersMapContent(
mountains = viewState.mountains,
)
}
MarkerType.Advanced -> {
AdvancedMarkersMapContent(
mountains = viewState.mountains,
)
}
MarkerType.Clustered -> {
ClusteringMarkersMapContent(
mountains = viewState.mountains,
)
}
}
}
İşaretçi ekleme
BasicMarkersMapContent
öğesine @GoogleMapComposable
ile not ekleyin. GoogleMap
içerik bloğunda yalnızca @GoogleMapComposable
işlevlerini kullanabileceğinizi unutmayın. mountains
nesnesi, Mountain
nesnelerinin listesini içerir. Mountain
nesnesindeki konum, ad ve yükseklik bilgilerini kullanarak listedeki her dağ için bir işaretçi eklersiniz. Konum, Marker
durum parametresini ayarlamak için kullanılır. Bu parametre de işaretçinin konumunu kontrol eder.
// ...
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
)
}
}
Uygulamayı çalıştırın. Eklediğiniz işaretçileri göreceksiniz.
İşaretçileri özelleştirme
Yeni eklediğiniz işaretçileri öne çıkarmanıza ve kullanıcılara faydalı bilgiler vermenize yardımcı olacak çeşitli özelleştirme seçenekleri vardır. Bu görevde, her işaretçinin resmini özelleştirerek bu özelliklerden bazılarını keşfedeceksiniz.
Başlangıç projesinde, @DrawableResource
öğesinden BitmapDescriptor
oluşturmak için vectorToBitmap
yardımcı işlevi bulunur.
Başlangıç kodunda, işaretçileri özelleştirmek için kullanacağınız bir dağ simgesi baseline_filter_hdr_24.xml
bulunur.
vectorToBitmap
işlevi, haritalar kitaplığıyla kullanılmak üzere bir drawable vektörü BitmapDescriptor
biçimine dönüştürür. Simge renkleri, BitmapParameters
örneği kullanılarak ayarlanır.
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 {
// ...
}
vectorToBitmap
işlevini kullanarak iki özelleştirilmiş BitmapDescriptor
oluşturun. Bunlardan biri 4.267 metreden yüksek dağlar, diğeri ise normal dağlar için olsun. Ardından, simgeyi ayarlamak için Marker
composable'ın icon
parametresini kullanın. Ayrıca, simgeye göre sabitleme konumunu değiştirmek için anchor
parametresini ayarlayın. Bu dairesel simgeler için ortayı kullanmak daha iyi sonuç verir.
@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,
)
}
}
Uygulamayı çalıştırın ve özelleştirilmiş işaretçilere göz atın. Dağların tamamını görmek için Show all
anahtarını açın. Dört bin metrelik dağlar, farklı işaretlere sahip olur.
10. Gelişmiş işaretçiler
AdvancedMarker
'ler, temel Markers
'e ekstra özellikler ekler. Bu adımda, çakışma davranışını ayarlayacak ve raptiye stilini yapılandıracaksınız.
@GoogleMapComposable
işlevine AdvancedMarkersMapContent
işlevini ekleyin. Her biri için AdvancedMarker
ekleyerek mountains
üzerinde döngü oluşturun.
@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
}
)
}
}
collisionBehavior
parametresine dikkat edin. Bu parametreyi REQUIRED_AND_HIDES_OPTIONAL
olarak ayarladığınızda işaretçiniz, daha düşük öncelikli tüm işaretçilerin yerini alır. Bunu, temel bir işaretleyiciyi gelişmiş bir işaretleyiciyle karşılaştırarak görebilirsiniz. Temel işaretçi, büyük olasılıkla hem işaretçinizi hem de işaretçinin temel haritada aynı konuma yerleştirilmiş halini içerir. Gelişmiş işaretleyici, daha düşük öncelikli işaretleyicinin gizlenmesine neden olur.
Gelişmiş işaretçileri görmek için uygulamayı çalıştırın. Alt gezinme satırında Advanced markers
sekmesini seçtiğinizden emin olun.
Özelleştirilmiş AdvancedMarkers
Simgelerde, 4.267 metreden yüksek dağlar ile diğer dağlar arasında ayrım yapmak için birincil ve ikincil renk şemaları kullanılır. vectorToBitmap
işlevini kullanarak iki BitmapDescriptor
oluşturun. Biri 4.267 metreden yüksek dağlar, diğeri ise diğer dağlar için olsun. Bu simgeleri kullanarak her tür için özel bir pinConfig
oluşturun. Son olarak, AdvancedMarker
işlevine göre pimi ilgili is14er()
'ye uygulayın.
@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. Gruplandırılmış işaretçiler
Bu adımda, yakınlaştırmaya dayalı öğe gruplandırma eklemek için Clustering
composable'ı kullanacaksınız.
Clustering
composable'ı için ClusterItem
koleksiyonu gerekir. MountainClusterItem
, ClusterItem
arayüzünü uygular. Bu sınıfı ClusteringMarkersMapContent.kt
dosyasına ekleyin.
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
}
Şimdi de dağ listesinden MountainClusterItem
oluşturmak için kodu ekleyin. Bu kodun, kullanıcının yerel ayarlarına göre uygun görüntüleme birimlerine dönüştürmek için UnitsConverter
kullandığını unutmayın. Bu, MainActivity
içinde CompositionLocal
kullanılarak ayarlanır.
@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,
)
}
Bu kodla işaretçiler, yakınlaştırma düzeyine göre gruplandırılır. Tertemiz!
Kümeleri özelleştirme
Diğer işaretçi türlerinde olduğu gibi, gruplandırılmış işaretçiler de özelleştirilebilir. Clustering
composable'ının clusterItemContent
parametresi, kümelenmemiş bir öğeyi oluşturmak için özel bir composable blok ayarlar. İşareti oluşturmak için bir @Composable
işlevi uygulayın. SingleMountain
işlevi, özelleştirilmiş bir arka plan renk şemasına sahip bir composable Material 3 Icon
oluşturur.
ClusteringMarkersMapContent.kt
içinde, bir işaretçinin renk şemasını tanımlayan bir veri sınıfı oluşturun:
data class IconColor(val iconColor: Color, val backgroundColor: Color, val borderColor: Color)
Ayrıca, ClusteringMarkersMapContent.kt
içinde belirli bir renk şeması için simge oluşturacak bir composable işlevi oluşturun:
@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)
)
}
Şimdi 4.267 metreden yüksek dağlar için bir renk şeması, diğer dağlar içinse başka bir renk şeması oluşturun. clusterItemContent
bloğunda, verilen dağın 4.267 metreden yüksek olup olmadığına göre renk düzenini seçin.
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)
},
)
}
Şimdi, tek tek öğelerin özelleştirilmiş sürümlerini görmek için uygulamayı çalıştırın.
12. Harita üzerinde çizin
Haritada çizim yapmanın bir yolunu (işaretçi ekleyerek) zaten incelemiş olsanız da Android için Haritalar SDK'sı, haritada yararlı bilgiler göstermek için kullanabileceğiniz birçok başka çizim yöntemini destekler.
Örneğin, haritada rotaları ve alanları göstermek istiyorsanız bunları haritada görüntülemek için Polyline
ve Polygon
simgelerini kullanabilirsiniz. Alternatif olarak, bir resmi yer yüzeyine sabitlemek istiyorsanız GroundOverlay
kullanabilirsiniz.
Bu görevde, şekil çizmeyi, özellikle de Colorado Eyaleti'nin etrafında bir ana hat oluşturmayı öğreneceksiniz. Colorado sınırı, 37°N ile 41°N enlemi ve 102°03'W ile 109°03'W arasında tanımlanır. Bu sayede ana hatları çizmek oldukça kolaylaşır.
Başlangıç kodu, derece-dakika-saniye gösteriminden ondalık dereceye dönüştürmek için bir DMS
sınıfı içerir.
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
DMS sınıfıyla, dört köşe LatLng
konumunu tanımlayıp bunları Polygon
olarak oluşturarak Colorado'nun sınırını çizebilirsiniz. Aşağıdaki kodu MountainMap.kt
dosyasına ekleyin.
@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),
)
}
Şimdi GoogleMap
içerik bloğunun içinde ColoradoPolyon()
işlevini çağırın.
@Composable
fun MountainMap(
// ...
) {
Box(
// ...
) {
GoogleMap(
// ...
) {
ColoradoPolygon()
}
}
}
Uygulama artık Colorado eyaletinin sınırlarını çiziyor ve eyaleti hafifçe dolduruyor.
13. KML katmanı ve ölçek çubuğu ekleme
Bu son bölümde, farklı dağ sıralarını kabaca özetleyecek ve haritaya bir ölçek çubuğu ekleyeceksiniz.
Sıradağları özetleyin
Daha önce Kolorado'nun etrafına bir ana hat çizdiniz. Burada haritaya daha karmaşık şekiller ekleyeceksiniz. Başlangıç kodu, önemli dağ sıralarını kabaca belirten bir Keyhole Biçimlendirme Dili (KML) dosyası içerir. Android için Haritalar SDK'sı Yardımcı Program Kitaplığı'nda haritaya KML katmanı ekleme işlevi bulunur. MountainMap.kt
bölümünde, when
bloğundan sonraki GoogleMap
içerik bloğuna MapEffect
çağrısı ekleyin. MapEffect
işlevi, GoogleMap
nesnesiyle çağrılır. Bu, GoogleMap
nesnesi gerektiren, birleştirilemeyen API'ler ve kitaplıklar arasında faydalı bir köprü görevi görebilir.
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()
}
}
Harita ölçeği ekleme
Son görev olarak haritaya ölçek ekleyeceksiniz. ScaleBar
, haritaya eklenebilen bir ölçek composable'ı uygular. ScaleBar
bir
@GoogleMapComposable
ve bu nedenle GoogleMap
içeriğine eklenemez. Bunun yerine, haritayı içeren Box
öğesine ekleyin.
Box(
// ...
) {
GoogleMap(
// ...
) {
// ...
}
ScaleBar(
modifier = Modifier
.padding(top = 5.dp, end = 15.dp)
.align(Alignment.TopEnd),
cameraPositionState = cameraPositionState
)
// ...
}
Tam olarak uygulanmış codelab'i görmek için uygulamayı çalıştırın.
14. Çözüm kodunu alma
Tamamlanmış codelab'in kodunu indirmek için aşağıdaki komutları kullanabilirsiniz:
git
yüklüyse depoyu klonlayın.
$ git clone https://github.com/googlemaps-samples/codelab-maps-platform-101-compose.git
Alternatif olarak, kaynak kodunu indirmek için aşağıdaki düğmeyi tıklayabilirsiniz.
- Kodu aldıktan sonra Android Studio'da
solution
dizinindeki projeyi açın.
15. Tebrikler
Tebrikler! Birçok konuya değindik. Umarım Android için Haritalar SDK'sında sunulan temel özellikler hakkında daha iyi bir anlayışa sahipsinizdir.
Daha fazla bilgi
- Android için Haritalar SDK'sı: Android uygulamalarınız için dinamik, etkileşimli, özelleştirilmiş haritalar, konum ve coğrafi deneyimler oluşturun.
- Maps Compose Kitaplığı: Uygulamanızı oluşturmak için Jetpack Compose ile kullanabileceğiniz bir dizi açık kaynaklı composable işlev ve veri türü.
- android-maps-compose: Bu kod laboratuvarında ele alınan tüm özellikleri ve daha fazlasını gösteren GitHub'daki örnek kod.
- Google Haritalar Platformu ile Android uygulamaları geliştirme hakkında daha fazla Kotlin codelab