1. Başlamadan Önce
Bu codelab'de, ABD'nin Kaliforniya eyaletindeki San Francisco şehrinde bulunan bisiklet mağazalarını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.
Ön koşullar
- Kotlin ve Android geliştirme hakkında temel düzeyde bilgi sahibi olmak
Yapacaklarınız
- Google Haritalar'ı bir Android uygulamasına eklemek için Android için Haritalar SDK'sını etkinleştirin ve kullanın.
- İşaretçileri ekleme, özelleştirme ve gruplandırma
- Haritada çoklu çizgiler ve poligonlar çizin.
- Kameranın bakış açısını programatik olarak kontrol edin.
İhtiyacınız olanlar
- Android için Haritalar SDK'sı
- Faturalandırmanın etkinleştirildiği bir Google Hesabı
- Android Studio 2020.3.1 veya sonraki sürümler
- Android Studio'da Google Play Hizmetleri'nin yüklü olması
- Android 4.2.2 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)
2. Hazırlanın
Aşağıdaki etkinleştirme adımı için 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/googlecodelabs/maps-platform-101-android.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. Google Haritalar'ı ekleme
Bu bölümde, uygulamayı başlattığınızda yüklenmesi için Google Haritalar'ı ekleyeceksiniz.
API anahtarınızı ekleme
Android için Haritalar SDK'sının anahtarınızı uygulamanızla ilişkilendirebilmesi için önceki bir adımda oluşturduğunuz API anahtarının uygulamaya sağlanması gerekir.
- Bunu sağlamak için projenizin kök dizininde (
gradle.properties
vesettings.gradle
ile aynı düzeyde)local.properties
adlı dosyayı açın. - Bu dosyada, değeri oluşturduğunuz API anahtarı olan yeni bir anahtar
GOOGLE_MAPS_API_KEY
tanımlayın.
local.properties
GOOGLE_MAPS_API_KEY=YOUR_KEY_HERE
local.properties
karakterinin, Git deposundaki .gitignore
dosyasında listelendiğini fark edeceksiniz. Bunun nedeni, API anahtarınızın hassas bilgi olarak kabul edilmesi ve mümkünse kaynak kontrolüne dahil edilmemesi gerektiğidir.
- Ardından, API'nizi uygulamanızda kullanılabilir hale getirmek için
app/
dizinindeki uygulamanızınbuild.gradle
dosyasına Secrets Gradle Plugin for Android eklentisini ekleyin veplugins
bloğuna aşağıdaki satırı ekleyin:
app-level build.gradle
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
Ayrıca, proje düzeyindeki build.gradle
dosyanızı aşağıdaki sınıf yolunu içerecek şekilde değiştirmeniz gerekir:
project-level build.gradle
buildscript {
dependencies {
// ...
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:1.3.0"
}
}
Bu eklenti, local.properties
dosyanızda tanımladığınız anahtarları derleme zamanında Android manifest dosyasında derleme değişkenleri ve Gradle tarafından oluşturulan BuildConfig
sınıfında değişkenler olarak kullanılabilir hale getirir. Bu eklentiyi kullandığınızda, local.properties
içindeki özelliklerin okunması için gerekli olan ve uygulamanızın her yerinden erişilebilen standart kod kaldırılır.
Google Haritalar bağımlılığı 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
dosyasına eklemektir.
Bu codelab ile birlikte gelen başlangıç projesinde bu bağımlılık sizin için zaten eklenmiştir.
build.gradle
dependencies {
// Dependency to include Maps SDK for Android
implementation 'com.google.android.gms:play-services-maps:17.0.0'
}
- Ardından, önceki adımda oluşturduğunuz API anahtarını iletmek için
AndroidManifest.xml
bölümüne yeni birmeta-data
etiketi ekleyin. Bunu yapmak için bu dosyayı Android Studio'da açın veapp/src/main
konumundakiAndroidManifest.xml
dosyanızdaapplication
nesnesinin içine aşağıdakimeta-data
etiketini ekleyin.
AndroidManifest.xml
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${GOOGLE_MAPS_API_KEY}" />
- Ardından,
app/src/main/res/layout/
dizinindeactivity_main.xml
adlı yeni bir düzen dosyası oluşturun ve bunu aşağıdaki gibi tanımlayın:
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<fragment
class="com.google.android.gms.maps.SupportMapFragment"
android:id="@+id/map_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Bu düzende, SupportMapFragment
içeren tek bir FrameLayout
var. Bu parça, sonraki adımlarda kullanacağınız temel GoogleMaps
nesnesini içerir.
- Son olarak,
MainActivity
sınıfınıapp/src/main/java/com/google/codelabs/buildyourfirstmap
konumunda güncelleyin. Bunun içinonCreate
yöntemini geçersiz kılacak aşağıdaki kodu ekleyerek içeriklerini yeni oluşturduğunuz düzenle ayarlayabilirsiniz.
MainActivity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
- Şimdi uygulamayı çalıştırın. Haritanın cihazınızın ekranına yüklendiğini görmeniz gerekir.
5. Bulut tabanlı harita stili (isteğe bağlı)
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.
Uygulamanıza harita kimliği ekleme
Oluşturduğunuz harita kimliğini kullanmak için activity_main.xml
dosyasını değiştirin ve harita kimliğinizi SupportMapFragment
öğesinin map:mapId
özelliğinde iletin.
activity_main.xml
<fragment xmlns:map="http://schemas.android.com/apk/res-auto"
class="com.google.android.gms.maps.SupportMapFragment"
<!-- ... -->
map:mapId="YOUR_MAP_ID" />
Bu işlemi tamamladıktan sonra, seçtiğiniz stildeki haritanızı görmek için uygulamayı çalıştırın.
6. İşaretçi ekleme
Bu görevde, haritada vurgulamak istediğiniz önemli yerleri temsil eden işaretçiler ekleyeceksiniz. Öncelikle, başlangıç projesinde sizin için sağlanan yerlerin listesini alıp bu yerleri haritaya eklersiniz. Bu örnekte, bunlar bisiklet dükkanlarıdır.
GoogleMap'e referans alma
Öncelikle, yöntemlerini kullanabilmek için GoogleMap
nesnesine bir referans almanız gerekir. Bunu yapmak için MainActivity.onCreate()
yönteminizde setContentView()
çağrısından hemen sonra aşağıdaki kodu ekleyin:
MainActivity.onCreate()
val mapFragment = supportFragmentManager.findFragmentById(
R.id.map_fragment
) as? SupportMapFragment
mapFragment?.getMapAsync { googleMap ->
addMarkers(googleMap)
}
Uygulama, SupportFragmentManager
nesnesinde findFragmentById()
yöntemini kullanarak önceki adımda eklediğiniz SupportMapFragment
öğesini önce bulur. Bir referans alındıktan sonra getMapAsync()
çağrısı yapılır ve ardından bir lambda iletilir. GoogleMap
nesnesinin iletildiği yer bu lambda'dır. Bu lambda içinde, kısa süre sonra tanımlanacak olan addMarkers()
yöntemi çağrısı başlatılır.
Sağlanan sınıf: PlacesReader
Başlangıç projesinde PlacesReader
sınıfı sizin için sağlanmıştır. Bu sınıf, places.json
adlı bir JSON dosyasında depolanan 49 yerin listesini okur ve bunları List<Place>
olarak döndürür. Yerler, San Francisco, CA, ABD'deki bisiklet mağazalarının listesini temsil etmektedir.
Bu sınıfın uygulanmasıyla ilgili merak ettikleriniz varsa sınıfa GitHub'dan erişebilir veya Android Studio'da PlacesReader
sınıfını açabilirsiniz.
PlacesReader
package com.google.codelabs.buildyourfirstmap.place
import android.content.Context
import com.google.codelabs.buildyourfirstmap.R
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
import java.io.InputStream
import java.io.InputStreamReader
/**
* Reads a list of place JSON objects from the file places.json
*/
class PlacesReader(private val context: Context) {
// GSON object responsible for converting from JSON to a Place object
private val gson = Gson()
// InputStream representing places.json
private val inputStream: InputStream
get() = context.resources.openRawResource(R.raw.places)
/**
* Reads the list of place JSON objects in the file places.json
* and returns a list of Place objects
*/
fun read(): List<Place> {
val itemType = object : TypeToken<List<PlaceResponse>>() {}.type
val reader = InputStreamReader(inputStream)
return gson.fromJson<List<PlaceResponse>>(reader, itemType).map {
it.toPlace()
}
}
Yerleri yükleme
Bisiklet dükkanlarının listesini yüklemek için MainActivity
içinde places
adlı bir özellik ekleyin ve bu özelliği aşağıdaki gibi tanımlayın:
MainActivity.places
private val places: List<Place> by lazy {
PlacesReader(this).read()
}
Bu kod, PlacesReader
üzerinde read()
yöntemini çağırır ve List<Place>
döndürür. Bir Place
, name
adlı bir özelliğe (yerin adı) ve latLng
adlı bir özelliğe (yerin bulunduğu koordinatlar) sahiptir.
Place
data class Place(
val name: String,
val latLng: LatLng,
val address: LatLng,
val rating: Float
)
Haritaya işaretçi ekleme
Yer listesi belleğe yüklendiğine göre, bir sonraki adım bu yerleri haritada göstermektir.
MainActivity
içindeaddMarkers()
adlı bir yöntem oluşturun ve aşağıdaki gibi tanımlayın:
MainActivity.addMarkers()
/**
* Adds marker representations of the places list on the provided GoogleMap object
*/
private fun addMarkers(googleMap: GoogleMap) {
places.forEach { place ->
val marker = googleMap.addMarker(
MarkerOptions()
.title(place.name)
.position(place.latLng)
)
}
}
Bu yöntem, places
listesinde yinelenir ve sağlanan GoogleMap
nesnesinde addMarker()
yöntemini çağırır. İşaretçi, MarkerOptions
nesnesi oluşturularak oluşturulur. Bu nesne, işaretçinin kendisini özelleştirmenize olanak tanır. Bu durumda, sırasıyla bisiklet dükkanının adını ve koordinatlarını temsil eden işaretin başlığı ve konumu sağlanır.
- Uygulamayı çalıştırın ve eklediğiniz işaretçileri görmek için San Francisco'ya gidin.
7. İş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 ve bir işaretçiye dokunulduğunda görüntülenen bilgi penceresini özelleştirerek bu işaretçilerden bazılarını keşfedeceksiniz.
Bilgi penceresi ekleme
Varsayılan olarak, bir işaretçiye dokunduğunuzda bilgi penceresinde başlığı ve snippet'i (ayarlanmışsa) gösterilir. Bunu, yerin adresi ve puanı gibi ek bilgileri gösterecek şekilde özelleştirirsiniz.
marker_info_contents.xml dosyasını oluşturun
Öncelikle marker_info_contents.xml
adlı yeni bir düzen dosyası oluşturun.
- Bunu yapmak için Android Studio'daki proje görünümünde
app/src/main/res/layout
klasörünü sağ tıklayın ve Yeni > Layout Resource File'ı (Düzen Kaynak Dosyası) seçin.
- İletişim kutusunda, Dosya adı alanına
marker_info_contents
,Root element
alanınaLinearLayout
yazıp Tamam'ı tıklayın.
Bu düzen dosyası daha sonra bilgi penceresindeki içerikleri temsil edecek şekilde genişletilir.
- Dikey
LinearLayout
görünüm grubuna üçTextViews
ekleyen aşağıdaki kod snippet'indeki içeriği kopyalayın ve dosyadaki varsayılan kodun üzerine yazın.
marker_info_contents.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="8dp">
<TextView
android:id="@+id/text_view_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="18sp"
android:textStyle="bold"
tools:text="Title"/>
<TextView
android:id="@+id/text_view_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="16sp"
tools:text="123 Main Street"/>
<TextView
android:id="@+id/text_view_rating"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textSize="16sp"
tools:text="Rating: 3"/>
</LinearLayout>
InfoWindowAdapter'ın bir uygulamasını oluşturma
Özel bilgi penceresi için düzen dosyasını oluşturduktan sonraki adım, GoogleMap.InfoWindowAdapter arayüzünü uygulamaktır. Bu arayüzde getInfoWindow()
ve getInfoContents()
olmak üzere iki yöntem bulunur. Her iki yöntem de isteğe bağlı bir View
nesnesi döndürür. Bu nesnelerden ilki pencerenin kendisini, ikincisi ise içeriğini özelleştirmek için kullanılır. Sizin durumunuzda, her ikisini de uygulayıp getInfoContents()
değerini döndürürken getInfoWindow()
değerini boş döndürerek varsayılan pencerenin kullanılması gerektiğini belirtiyorsunuz.
- Android Studio'daki proje görünümünde
app/src/main/java/com/google/codelabs/buildyourfirstmap
klasörünü sağ tıklayıp Yeni > Kotlin Dosyası/Sınıfı'nı seçerekMainActivity
ile aynı paketteMarkerInfoWindowAdapter
adlı yeni bir Kotlin dosyası oluşturun.
- İletişim kutusunda
MarkerInfoWindowAdapter
yazın ve Dosya'yı vurgulanmış halde tutun.
- Dosyayı oluşturduktan sonra aşağıdaki kod snippet'indeki içerikleri yeni dosyanıza kopyalayın.
MarkerInfoWindowAdapter
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.widget.TextView
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.model.Marker
import com.google.codelabs.buildyourfirstmap.place.Place
class MarkerInfoWindowAdapter(
private val context: Context
) : GoogleMap.InfoWindowAdapter {
override fun getInfoContents(marker: Marker?): View? {
// 1. Get tag
val place = marker?.tag as? Place ?: return null
// 2. Inflate view and set title, address, and rating
val view = LayoutInflater.from(context).inflate(
R.layout.marker_info_contents, null
)
view.findViewById<TextView>(
R.id.text_view_title
).text = place.name
view.findViewById<TextView>(
R.id.text_view_address
).text = place.address
view.findViewById<TextView>(
R.id.text_view_rating
).text = "Rating: %.2f".format(place.rating)
return view
}
override fun getInfoWindow(marker: Marker?): View? {
// Return null to indicate that the
// default window (white bubble) should be used
return null
}
}
getInfoContents()
yönteminin içeriklerinde, yöntemde sağlanan işaretçi Place
türüne dönüştürülür. Dönüştürme mümkün değilse yöntem null değerini döndürür (Marker
üzerinde henüz etiket özelliğini ayarlamadınız, ancak bunu sonraki adımda yapacaksınız).
Ardından, düzen marker_info_contents.xml
genişletilir ve metin, TextViews
öğesinde Place
etiketine ayarlanır.
MainActivity'yi güncelleme
Şimdiye kadar oluşturduğunuz tüm bileşenleri birleştirmek için MainActivity
sınıfınıza iki satır eklemeniz gerekir.
Öncelikle, özel InfoWindowAdapter
, MarkerInfoWindowAdapter
değerini getMapAsync
yöntem çağrısında iletmek için GoogleMap
nesnesinde setInfoWindowAdapter()
yöntemini çağırın ve MarkerInfoWindowAdapter
öğesinin yeni bir örneğini oluşturun.
- Bunu yapmak için
addMarkers()
yöntem çağrısından sonragetMapAsync()
lambda'sının içine aşağıdaki kodu ekleyin.
MainActivity.onCreate()
// Set custom info window adapter
googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
Son olarak, haritaya eklenen her işaretçide her bir yeri etiket özelliği olarak ayarlamanız gerekir.
- Bunu yapmak için
places.forEach{}
işlevindekiaddMarkers()
çağrısını aşağıdaki şekilde değiştirin:
MainActivity.addMarkers()
places.forEach { place ->
val marker = googleMap.addMarker(
MarkerOptions()
.title(place.name)
.position(place.latLng)
.icon(bicycleIcon)
)
// Set place as the tag on the marker object so it can be referenced within
// MarkerInfoWindowAdapter
marker.tag = place
}
Özel işaretçi resmi ekleme
İşaretçi resmini özelleştirmek, işaretçinin haritanızda temsil ettiği yer türünü belirtmenin eğlenceli yollarından biridir. Bu adımda, haritadaki her bir mağazayı temsil etmek için varsayılan kırmızı işaretçiler yerine bisikletler gösterilir. Başlangıç projesinde, kullandığınız app/src/res/drawable
içinde bisiklet simgesi ic_directions_bike_black_24dp.xml
bulunur.
İşaretçide özel bit eşlem ayarlama
Kullanabileceğiniz vektör çizilebilir bisiklet simgesiyle bir sonraki adım, bu çizilebilir öğeyi haritadaki her işaretçinin simgesi olarak ayarlamaktır. MarkerOptions
, bu işlemi gerçekleştirmek için kullandığınız bir BitmapDescriptor
alan icon
yöntemine sahiptir.
Öncelikle, yeni eklediğiniz vektör çizilebilir öğeyi BitmapDescriptor
. Başlangıç projesine dahil edilen BitMapHelper
adlı dosyada, tam olarak bunu yapan vectorToBitmap()
adlı bir yardımcı işlev bulunur.
BitmapHelper
package com.google.codelabs.buildyourfirstmap
import android.content.Context
import android.graphics.Bitmap
import android.graphics.Canvas
import android.util.Log
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory
object BitmapHelper {
/**
* Demonstrates converting a [Drawable] to a [BitmapDescriptor],
* for use as a marker icon. Taken from ApiDemos on GitHub:
* https://github.com/googlemaps/android-samples/blob/main/ApiDemos/kotlin/app/src/main/java/com/example/kotlindemos/MarkerDemoActivity.kt
*/
fun vectorToBitmap(
context: Context,
@DrawableRes id: Int,
@ColorInt color: Int
): BitmapDescriptor {
val vectorDrawable = ResourcesCompat.getDrawable(context.resources, id, null)
if (vectorDrawable == null) {
Log.e("BitmapHelper", "Resource not found")
return BitmapDescriptorFactory.defaultMarker()
}
val bitmap = Bitmap.createBitmap(
vectorDrawable.intrinsicWidth,
vectorDrawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
vectorDrawable.setBounds(0, 0, canvas.width, canvas.height)
DrawableCompat.setTint(vectorDrawable, color)
vectorDrawable.draw(canvas)
return BitmapDescriptorFactory.fromBitmap(bitmap)
}
}
Bu yöntem, Context
, çizilebilir kaynak kimliği ve renk tamsayısı alır ve bunun BitmapDescriptor
gösterimini oluşturur.
Yardımcı yöntemi kullanarak bicycleIcon
adlı yeni bir özellik bildirin ve bu özelliğe şu tanımı verin: MainActivity.bicycleIcon
private val bicycleIcon: BitmapDescriptor by lazy {
val color = ContextCompat.getColor(this, R.color.colorPrimary)
BitmapHelper.vectorToBitmap(this, R.drawable.ic_directions_bike_black_24dp, color)
}
Bu özellik, uygulamanızda önceden tanımlanmış colorPrimary
rengini kullanır ve bisiklet simgesini bu renkle renklendirip BitmapDescriptor
olarak döndürür.
- Bu özelliği kullanarak simge özelleştirmenizi tamamlamak için
icon
yönteminiMarkerOptions
yönteminde çağırın.addMarkers()
Bu durumda işaretçi özelliği şu şekilde görünmelidir:
MainActivity.addMarkers()
val marker = googleMap.addMarker(
MarkerOptions()
.title(place.name)
.position(place.latLng)
.icon(bicycleIcon)
)
- Güncellenen işaretçileri görmek için uygulamayı çalıştırın.
8. Küme işaretçileri
Haritayı ne kadar yakınlaştırdığınıza bağlı olarak, eklediğiniz işaretçilerin çakıştığını fark etmiş olabilirsiniz. Çakışan işaretçilerle etkileşim kurmak çok zordur ve çok fazla gürültü oluşturarak uygulamanızın kullanılabilirliğini etkiler.
Bu durumda kullanıcı deneyimini iyileştirmek için, yakından kümelenmiş büyük bir veri kümeniz olduğunda işaretçi kümelemeyi uygulamak en iyi yöntemdir. Kümeleme özelliği sayesinde, haritayı yakınlaştırıp uzaklaştırdığınızda birbirine yakın işaretçiler aşağıdaki gibi kümelenir:
Bunu uygulamak için Android için Haritalar SDK'sı Yardımcı Kitaplığı'nın yardımına ihtiyacınız vardır.
Android için Haritalar SDK'sı Yardımcı Program Kitaplığı
Android için Haritalar SDK'sı Yardımcı Kitaplığı, Android için Haritalar SDK'sının işlevselliğini genişletmek amacıyla oluşturulmuştur. İşaretçi kümeleme, ısı haritaları, KML ve GeoJson desteği, çoklu çizgi kodlama ve kod çözme gibi gelişmiş özellikler ile küresel geometriyle ilgili çeşitli yardımcı işlevler sunar.
build.gradle dosyanızı güncelleme
Yardımcı program kitaplığı, Android için Haritalar SDK'sından ayrı olarak paketlendiğinden build.gradle
dosyanıza ek bir bağımlılık eklemeniz gerekir.
app/build.gradle
dosyanızındependencies
bölümünü güncelleyin.
build.gradle
implementation 'com.google.maps.android:android-maps-utils:1.1.0'
- Bu satırı ekledikten sonra yeni bağımlılıkları getirmek için proje senkronizasyonu yapmanız gerekir.
Kümelemeyi uygulama
Uygulamanızda kümelemeyi uygulamak için şu üç adımı uygulayın:
ClusterItem
arayüzünü uygulayın.DefaultClusterRenderer
sınıfını alt sınıfa ayırın.ClusterManager
oluşturun ve öğe ekleyin.
ClusterItem arayüzünü uygulama
Haritada kümelenebilir bir işaretçiyi temsil eden tüm nesneler ClusterItem
arayüzünü uygulamalıdır. Bu durumda, Place
modelinin ClusterItem
ile uyumlu olması gerekir. Place.kt
dosyasını açın ve aşağıdaki değişiklikleri yapın:
Place
data class Place(
val name: String,
val latLng: LatLng,
val address: String,
val rating: Float
) : ClusterItem {
override fun getPosition(): LatLng =
latLng
override fun getTitle(): String =
name
override fun getSnippet(): String =
address
}
ClusterItem bu üç yöntemi tanımlar:
getPosition()
, yerinLatLng
değerini gösterir.getTitle()
: Yer adını gösterir.getSnippet()
, yerin adresini gösterir.
DefaultClusterRenderer sınıfını alt sınıflandırma
Kümelemeyi uygulamaktan sorumlu olan ClusterManager
sınıfı, haritada kaydırma ve yakınlaştırma yaparken kümelerin oluşturulmasını yönetmek için dahili olarak bir ClusterRenderer
sınıfı kullanır. Varsayılan olarak, ClusterRenderer
'ı uygulayan varsayılan oluşturucu DefaultClusterRenderer
ile birlikte gelir. Basit durumlarda bu yeterli olacaktır. Ancak sizin durumunuzda işaretçilerin özelleştirilmesi gerektiğinden bu sınıfı genişletmeniz ve özelleştirmeleri buraya eklemeniz gerekir.
com.google.codelabs.buildyourfirstmap.place
paketinde PlaceRenderer.kt
Kotlin dosyasını oluşturun ve aşağıdaki gibi tanımlayın:
PlaceRenderer
package com.google.codelabs.buildyourfirstmap.place
import android.content.Context
import androidx.core.content.ContextCompat
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import com.google.codelabs.buildyourfirstmap.BitmapHelper
import com.google.codelabs.buildyourfirstmap.R
import com.google.maps.android.clustering.ClusterManager
import com.google.maps.android.clustering.view.DefaultClusterRenderer
/**
* A custom cluster renderer for Place objects.
*/
class PlaceRenderer(
private val context: Context,
map: GoogleMap,
clusterManager: ClusterManager<Place>
) : DefaultClusterRenderer<Place>(context, map, clusterManager) {
/**
* The icon to use for each cluster item
*/
private val bicycleIcon: BitmapDescriptor by lazy {
val color = ContextCompat.getColor(context,
R.color.colorPrimary
)
BitmapHelper.vectorToBitmap(
context,
R.drawable.ic_directions_bike_black_24dp,
color
)
}
/**
* Method called before the cluster item (the marker) is rendered.
* This is where marker options should be set.
*/
override fun onBeforeClusterItemRendered(
item: Place,
markerOptions: MarkerOptions
) {
markerOptions.title(item.name)
.position(item.latLng)
.icon(bicycleIcon)
}
/**
* Method called right after the cluster item (the marker) is rendered.
* This is where properties for the Marker object should be set.
*/
override fun onClusterItemRendered(clusterItem: Place, marker: Marker) {
marker.tag = clusterItem
}
}
Bu sınıf, şu iki işlevi geçersiz kılar:
onBeforeClusterItemRendered()
, küme haritada oluşturulmadan önce çağrılır. Burada,MarkerOptions
aracılığıyla özelleştirmeler sağlayabilirsiniz. Bu durumda, işaretçinin başlığı, konumu ve simgesi ayarlanır.onClusterItemRenderer()
, işaretçi haritada oluşturulduktan hemen sonra çağrılır. OluşturulanMarker
nesnesine buradan erişebilirsiniz. Bu örnekte, işaretçinin etiket özelliği ayarlanır.
ClusterManager oluşturma ve öğe ekleme
Son olarak, kümelemeyi çalıştırmak için MainActivity
öğesini değiştirerek bir ClusterManager
oluşturmanız ve gerekli bağımlılıkları sağlamanız gerekir. ClusterManager
, işaretçilerin (ClusterItem
nesneleri) eklenmesini dahili olarak yönetir. Bu nedenle, işaretçileri doğrudan haritaya eklemek yerine bu sorumluluk ClusterManager
'ya devredilir. Ayrıca, ClusterManager
dahili olarak setInfoWindowAdapter()
yöntemini de çağırdığı için özel bilgi penceresi ayarlama işlemi ClusterManger
'nin MarkerManager.Collection
nesnesinde yapılmalıdır.
- Başlamak için
MainActivity.onCreate()
içindekigetMapAsync()
çağrısında lambda'nın içeriğini değiştirin.addMarkers()
vesetInfoWindowAdapter()
çağrısını yorum satırı haline getirin ve bunun yerine, bir sonraki adımda tanımlayacağınızaddClusteredMarkers()
adlı bir yöntemi çağırın.
MainActivity.onCreate()
mapFragment?.getMapAsync { googleMap ->
//addMarkers(googleMap)
addClusteredMarkers(googleMap)
// Set custom info window adapter.
// googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
}
- Ardından,
MainActivity
bölümündeaddClusteredMarkers()
öğesini tanımlayın.
MainActivity.addClusteredMarkers()
/**
* Adds markers to the map with clustering support.
*/
private fun addClusteredMarkers(googleMap: GoogleMap) {
// Create the ClusterManager class and set the custom renderer.
val clusterManager = ClusterManager<Place>(this, googleMap)
clusterManager.renderer =
PlaceRenderer(
this,
googleMap,
clusterManager
)
// Set custom info window adapter
clusterManager.markerCollection.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
// Add the places to the ClusterManager.
clusterManager.addItems(places)
clusterManager.cluster()
// Set ClusterManager as the OnCameraIdleListener so that it
// can re-cluster when zooming in and out.
googleMap.setOnCameraIdleListener {
clusterManager.onCameraIdle()
}
}
Bu yöntem, ClusterManager
öğesini oluşturur, özel oluşturucuyu PlacesRenderer
öğesine iletir, tüm yerleri ekler ve cluster()
yöntemini çağırır. Ayrıca, ClusterManager
, harita nesnesinde setInfoWindowAdapter()
yöntemini kullandığından özel bilgi penceresinin ayarlanması ClusterManager.markerCollection
nesnesinde yapılmalıdır. Son olarak, kullanıcı haritada kaydırma ve yakınlaştırma yaptığında kümelemenin değişmesini istediğiniz için OnCameraIdleListener
, googleMap
'ye sağlanır. Böylece kamera boşta kaldığında clusterManager.onCameraIdle()
çağrılır.
- Yeni gruplandırılmış mağazaları görmek için uygulamayı çalıştırın.
9. 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 çoklu çizgiler ve poligonlar kullanabilirsiniz. Alternatif olarak, bir görüntüyü yer yüzeyine sabitlemek isterseniz yer paylaşımlarını kullanabilirsiniz.
Bu görevde, bir işaretçiye dokunulduğunda etrafına şekil (özellikle daire) çizmeyi öğreneceksiniz.
Tıklama işleyici ekleme
Genellikle, bir işaretçiye tıklama işleyici eklemenin yolu, GoogleMap
nesnesine setOnMarkerClickListener()
üzerinden doğrudan bir tıklama işleyici iletmektir. Ancak kümeleme kullandığınız için tıklama işleyicinin ClusterManager
öğesine sağlanması gerekir.
addClusteredMarkers()
yöntemindeMainActivity
,cluster()
için çağırmanın hemen ardından aşağıdaki satırı ekleyin.
MainActivity.addClusteredMarkers()
// Show polygon
clusterManager.setOnClusterItemClickListener { item ->
addCircle(googleMap, item)
return@setOnClusterItemClickListener false
}
Bu yöntem, bir dinleyici ekler ve ardından tanımlayacağınız addCircle()
yöntemini çağırır. Son olarak, bu yöntemin etkinliği kullanmadığını belirtmek için bu yöntemden false
değeri döndürülür.
- Ardından,
circle
özelliğini veaddCircle()
yönteminiMainActivity
içinde tanımlamanız gerekir.
MainActivity.addCircle()
private var circle: Circle? = null
/**
* Adds a [Circle] around the provided [item]
*/
private fun addCircle(googleMap: GoogleMap, item: Place) {
circle?.remove()
circle = googleMap.addCircle(
CircleOptions()
.center(item.latLng)
.radius(1000.0)
.fillColor(ContextCompat.getColor(this, R.color.colorPrimaryTranslucent))
.strokeColor(ContextCompat.getColor(this, R.color.colorPrimary))
)
}
circle
özelliği, yeni bir işaretçiye her dokunulduğunda önceki dairenin kaldırılıp yeni bir dairenin ekleneceği şekilde ayarlanır. Daire eklemeye yönelik API'nin, işaretçi eklemeye yönelik API'ye oldukça benzediğini unutmayın.
- Şimdi uygulamayı çalıştırarak değişiklikleri görebilirsiniz.
10. Kamera Kontrolü
Son görev olarak, görünümü belirli bir bölgeye odaklayabilmek için bazı kamera kontrollerine bakıyorsunuz.
Kamera ve görünüm
Uygulamayı çalıştırdığınızda kameranın Afrika kıtasını gösterdiğini ve eklediğiniz işaretçileri bulmak için San Francisco'ya zahmetli bir şekilde kaydırma ve yakınlaştırma yapmanız gerektiğini fark ettiniz. Dünyayı keşfetmek için eğlenceli bir yöntem olsa da işaretçileri hemen göstermek istiyorsanız bu yöntem işe yaramaz.
Bunu kolaylaştırmak için kameranın konumunu programatik olarak ayarlayarak görünümü istediğiniz yerde ortalayabilirsiniz.
- Uygulama başlatıldığında kamera görünümünü San Francisco'ya göre başlatılacak şekilde ayarlamak için aşağıdaki kodu
getMapAsync()
çağrısına ekleyin.
MainActivity.onCreate()
mapFragment?.getMapAsync { googleMap ->
// Ensure all places are visible in the map.
googleMap.setOnMapLoadedCallback {
val bounds = LatLngBounds.builder()
places.forEach { bounds.include(it.latLng) }
googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 20))
}
}
İlk olarak, kamera güncellemesinin yalnızca harita yüklendikten sonra yapılması için setOnMapLoadedCallback()
çağrılır. Boyutlar gibi harita özelliklerinin kamera güncelleme çağrısı yapılmadan önce hesaplanması gerektiğinden bu adım gereklidir.
Lambda'da, haritada dikdörtgen bir bölgeyi tanımlayan yeni bir LatLngBounds
nesnesi oluşturulur. Bu, tüm yerlerin sınırlar içinde olduğundan emin olmak için tüm yer LatLng
değerlerini içerecek şekilde kademeli olarak oluşturulur. Bu nesne oluşturulduktan sonra GoogleMap
üzerindeki moveCamera()
yöntemi çağrılır ve CameraUpdateFactory.newLatLngBounds(bounds.build(), 20)
aracılığıyla CameraUpdate
sağlanır.
- Uygulamayı çalıştırın ve kameranın artık San Francisco'da başlatıldığını fark edin.
Kameradaki değişiklikleri dinleme
Kamera konumunu değiştirmenin yanı sıra, kullanıcı haritada hareket ederken kamera güncellemelerini de dinleyebilirsiniz. Kamera hareket ederken kullanıcı arayüzünü değiştirmek istiyorsanız bu özellik yararlı olabilir.
Eğlence amaçlı olarak, kamera her hareket ettirildiğinde işaretçileri yarı saydam hale getirmek için kodu değiştiriyorsunuz.
addClusteredMarkers()
yönteminde, yöntemin en altına aşağıdaki satırları ekleyin:
MainActivity.addClusteredMarkers()
// When the camera starts moving, change the alpha value of the marker to translucent.
googleMap.setOnCameraMoveStartedListener {
clusterManager.markerCollection.markers.forEach { it.alpha = 0.3f }
clusterManager.clusterMarkerCollection.markers.forEach { it.alpha = 0.3f }
}
Bu, OnCameraMoveStartedListener
ekler. Böylece kamera hareket etmeye başladığında tüm işaretçilerin (hem kümeler hem de işaretçiler) alfa değerleri 0.3f
olarak değiştirilir ve işaretçiler yarı saydam görünür.
- Son olarak, kamera durduğunda yarı saydam işaretçileri tekrar opak hale getirmek için
addClusteredMarkers()
yöntemindekisetOnCameraIdleListener
içeriğini aşağıdaki gibi değiştirin:
MainActivity.addClusteredMarkers()
googleMap.setOnCameraIdleListener {
// When the camera stops moving, change the alpha value back to opaque.
clusterManager.markerCollection.markers.forEach { it.alpha = 1.0f }
clusterManager.clusterMarkerCollection.markers.forEach { it.alpha = 1.0f }
// Call clusterManager.onCameraIdle() when the camera stops moving so that reclustering
// can be performed when the camera stops moving.
clusterManager.onCameraIdle()
}
- Sonuçları görmek için uygulamayı çalıştırın.
11. Maps KTX
Bir veya daha fazla Google Haritalar Platformu Android SDK'sı kullanan Kotlin uygulamalarında, Kotlin dil özelliklerinden (ör. eşzamanlı rutinler, uzantı özellikleri/işlevleri) yararlanabilmeniz için Kotlin uzantısı veya KTX kitaplıkları kullanılabilir. Her Google Haritalar SDK'sının, aşağıda gösterildiği gibi karşılık gelen bir KTX kitaplığı vardır:
Bu görevde, uygulamanıza Maps KTX ve Maps Utils KTX kitaplıklarını ekleyecek ve önceki görevlerdeki uygulamaları yeniden düzenleyerek uygulamanızda Kotlin'e özgü dil özelliklerini kullanabileceksiniz.
- KTX bağımlılıklarını uygulama düzeyindeki build.gradle dosyanıza ekleyin
Uygulama hem Android için Haritalar SDK'sını hem de Android için Haritalar SDK'sı Yardımcı Kitaplığı'nı kullandığından bu kitaplıklar için ilgili KTX kitaplıklarını eklemeniz gerekir. Bu görevde AndroidX Lifecycle KTX kitaplığında bulunan bir özelliği de kullanacağınız için bu bağımlılığı uygulama düzeyindeki build.gradle
dosyanıza da ekleyin.
build.gradle
dependencies {
// ...
// Maps SDK for Android KTX Library
implementation 'com.google.maps.android:maps-ktx:3.0.0'
// Maps SDK for Android Utility Library KTX Library
implementation 'com.google.maps.android:maps-utils-ktx:3.0.0'
// Lifecycle Runtime KTX Library
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'
}
- GoogleMap.addMarker() ve GoogleMap.addCircle() uzantı işlevlerini kullanma
Maps KTX kitaplığı, önceki adımlarda kullanılan GoogleMap.addMarker(MarkerOptions)
ve GoogleMap.addCircle(CircleOptions)
için DSL tarzı bir API alternatifi sunar. Yukarıda belirtilen API'leri kullanmak için işaretçi veya daire seçeneklerini içeren bir sınıf oluşturmak gerekirken KTX alternatiflerinde işaretçi veya daire seçeneklerini sağladığınız lambda'da ayarlayabilirsiniz.
Bu API'leri kullanmak için MainActivity.addMarkers(GoogleMap)
ve MainActivity.addCircle(GoogleMap)
yöntemlerini güncelleyin:
MainActivity.addMarkers(GoogleMap)
/**
* Adds markers to the map. These markers won't be clustered.
*/
private fun addMarkers(googleMap: GoogleMap) {
places.forEach { place ->
val marker = googleMap.addMarker {
title(place.name)
position(place.latLng)
icon(bicycleIcon)
}
// Set place as the tag on the marker object so it can be referenced within
// MarkerInfoWindowAdapter
marker.tag = place
}
}
MainActivity.addCircle(GoogleMap)
/**
* Adds a [Circle] around the provided [item]
*/
private fun addCircle(googleMap: GoogleMap, item: Place) {
circle?.remove()
circle = googleMap.addCircle {
center(item.latLng)
radius(1000.0)
fillColor(ContextCompat.getColor(this@MainActivity, R.color.colorPrimaryTranslucent))
strokeColor(ContextCompat.getColor(this@MainActivity, R.color.colorPrimary))
}
}
Yukarıdaki yöntemleri bu şekilde yeniden yazmak, Kotlin'in alıcılı işlev değişmezi kullanılarak mümkün kılınan, okunması çok daha kısa ve öz bir yöntemdir.
- SupportMapFragment.awaitMap() ve GoogleMap.awaitMapLoad() uzantı askıya alma işlevlerini kullanma
Maps KTX kitaplığı, coroutine'lerde kullanılacak askıya alma işlevi uzantıları da sağlar. Özellikle SupportMapFragment.getMapAsync(OnMapReadyCallback)
ve GoogleMap.setOnMapLoadedCallback(OnMapLoadedCallback)
için askıya alma işlevi alternatifleri vardır. Bu alternatif API'leri kullandığınızda geri çağırma işlevlerini iletmeniz gerekmez. Bunun yerine, bu yöntemlerin yanıtını seri ve senkron bir şekilde alabilirsiniz.
Bu yöntemler işlevleri askıya aldığından, bunların kullanımı bir ortak yordam içinde gerçekleşmelidir. Lifecycle Runtime KTX kitaplığı, uygun yaşam döngüsü etkinliğinde çalıştırılıp durdurulmaları için yaşam döngüsünden haberdar coroutine kapsamları sağlayan bir uzantı sunar.
Bu kavramları birleştirerek MainActivity.onCreate(Bundle)
yöntemini güncelleyin:
MainActivity.onCreate(Bundle)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mapFragment =
supportFragmentManager.findFragmentById(R.id.map_fragment) as SupportMapFragment
lifecycleScope.launchWhenCreated {
// Get map
val googleMap = mapFragment.awaitMap()
// Wait for map to finish loading
googleMap.awaitMapLoad()
// Ensure all places are visible in the map
val bounds = LatLngBounds.builder()
places.forEach { bounds.include(it.latLng) }
googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 20))
addClusteredMarkers(googleMap)
}
}
lifecycleScope.launchWhenCreated
coroutine kapsamı, etkinlik en az oluşturulmuş durumdayken bloğu yürütür. Ayrıca, GoogleMap
nesnesini almak ve haritanın yüklenmesinin tamamlanmasını beklemek için yapılan çağrıların sırasıyla SupportMapFragment.awaitMap()
ve GoogleMap.awaitMapLoad()
ile değiştirildiğini de unutmayın. Bu askıya alma işlevlerini kullanarak kodu yeniden düzenlemek, eşdeğer geri çağırma tabanlı kodu sıralı bir şekilde yazmanızı sağlar.
- Uygulamayı yeniden düzenlenmiş değişikliklerinizle yeniden oluşturabilirsiniz.
12. 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 Yerler SDK'sı: Çevrenizdeki işletmeleri keşfetmek için zengin yer verileri setini inceleyin.
- android-maps-ktx: Android için Haritalar SDK'sı ve Android için Haritalar SDK'sı Yardımcı Kitaplığı ile Kotlin dostu bir şekilde entegrasyon yapmanıza olanak tanıyan açık kaynaklı bir kitaplık.
- android-place-ktx: Android için Yerler SDK'sını Kotlin dostu bir şekilde entegre etmenize olanak tanıyan açık kaynaklı bir kitaplık.
- android-samples: Bu codelab'de ele alınan tüm özelliklerin ve daha fazlasının gösterildiği GitHub'daki örnek kod.
- Google Haritalar Platformu ile Android uygulamaları geliştirme hakkında daha fazla Kotlin codelab