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 GoogleMapeklemek 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.
- gityü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 starterdizinindeki 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.ktsdosyanızı açın vebuildscriptaltı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.ktsdosyanı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.ktsdosyanızdatargetSdkvecompileSdkdeğerlerinin en az 34 olarak ayarlandığından emin olun.
- Dosyayı kaydedin ve projenizi Gradle ile senkronize edin.
- secrets.propertiesdosyasını en üst düzey dizininizde açın ve aşağıdaki kodu ekleyin.- YOUR_API_KEYkı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.propertiesdosyasını üst düzey dizininizde (- secrets.propertiesdosyasıyla aynı klasörde) oluşturun ve aşağıdaki kodu ekleyin.- MAPS_API_KEY=DEFAULT_API_KEY- secrets.propertiesdosyası 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 bir- secrets.propertiesdosyası oluşturmadığınızda meydana gelir.
- Dosyayı kaydedin.
- AndroidManifest.xmldosyanızda- com.google.android.geo.API_KEYbölümüne gidin ve- android: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.ktsdosyanızı açın vesecretsözelliğini düzenleyin.secretsözelliği yoksa ekleyin.Eklentinin özelliklerini düzenleyerekpropertiesFileNameözelliğinisecrets.properties,defaultPropertiesFileNameözelliğinilocal.defaults.propertiesve 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:
- gityü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 solutiondizinindeki 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





