1. Başlamadan Önce
Bu codelab'de, ABD için San Francisco, CA'daki bisiklet mağazalarının haritasını görüntüleyen bir uygulama oluşturarak Android için Haritalar SDK'sını uygulamanızla nasıl entegre edeceğinizi ve temel özelliklerini nasıl kullanacağınızı öğreneceksiniz.
Ön koşullar
- Kotlin ve Android geliştirme ile ilgili temel bilgiler
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çi ekleyin, özelleştirin ve kümeleyin.
- Haritaya çoklu çizgi ve poligon çizin.
- Kameranın görüş noktasını programatik olarak kontrol edin.
Gerekenler
- Android için Haritalar SDK'sı
- Faturalandırmanın etkin olduğu bir Google Hesabı
- Android Studio 2020.3.1 veya sonraki sürümler
- Android Studio'da yüklü olan Google Play hizmetleri
- Android 4.2.2 veya sonraki bir sürüme dayalı olarak Google API'leri platformunu çalıştıran bir Android cihaz veya Android emülatörü (yükleme adımları için Android Emulator'da uygulama çalıştırma konusuna bakın).
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 kurma
Henüz bir Google Cloud Platform hesabınız ve faturalandırmanın etkin olduğu bir projeniz yoksa faturalandırma hesabı ve proje oluşturmak için lütfen 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.
- Google Cloud Marketplace'te bu codelab için gerekli olan Google Haritalar Platformu API'lerini ve SDK'larını etkinleştirin. Bunu yapmak için bu video veya bu dokümanlardaki adımları uygulayın.
- Cloud Console'un Kimlik Bilgileri sayfasında API anahtarı oluşturun. Bu video veya bu dokümanlardaki adımları uygulayabilirsiniz. Google Haritalar Platformu'na gönderilen tüm istekler bir API anahtarı gerektirir.
3. Hızlı başlangıç
En kısa sürede başlamanıza yardımcı olmak için aşağıda, bu codelab'i takip etmenize yardımcı olacak bazı başlangıç kodları verilmiştir. Çözüme geçebilirsiniz, ancak çözümü derlemek için tüm adımları izlemek isterseniz okumaya devam edin.
git
dosyasını yüklediyseniz depoyu klonlayın.
git clone https://github.com/googlecodelabs/maps-platform-101-android.git
Alternatif olarak, kaynak kodu indirmek için aşağıdaki düğmeyi tıklayabilirsiniz.
- Kodu aldıktan sonra Android Studio'daki
starter
dizininde bulunan projeyi açın.
4. Google Haritalar'ı Ekleyin
Bu bölümde, Google Haritalar'ı uygulamayı başlattığınızda yüklenmesi için eklersiniz.
API anahtarınızı ekleme
Android için Haritalar SDK'sının anahtarınızı uygulamanızla ilişkilendirebilmesi için daha önceki adımlarda oluşturduğunuz API anahtarının uygulamaya sağlanması gerekir.
- Bunu sağlamak için projenizin kök dizininde (
gradle.properties
vesettings.gradle
'nin yer aldığı seviyede)local.properties
adlı dosyayı açın. - Bu dosyada, değeri oluşturduğunuz API anahtarı olacak şekilde yeni bir
GOOGLE_MAPS_API_KEY
anahtarı tanımlayın.
local.properties
GOOGLE_MAPS_API_KEY=YOUR_KEY_HERE
local.properties
değerinin Git deposundaki .gitignore
dosyasında listelendiğine dikkat edin. 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ılabilmesi için açığa çıkarmak amacıyla uygulamanızın
app/
dizininde bulunan Secrets Gradle Plugin for Android eklentisini dahil edin ve aşağıdaki satırıplugins
blokuna ekleyin:
uygulama düzeyinde build.gradle
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
Proje düzeyindeki build.gradle
dosyanızı aşağıdaki sınıf yolunu içerecek şekilde de değiştirmeniz gerekir:
proje düzeyinde 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ı, Android manifest dosyasında derleme değişkenleri olarak ve derleme sırasında Gradle tarafından oluşturulan BuildConfig
sınıfında değişkenler olarak kullanılabilir hale getirir. Bu eklentinin kullanılması, local.properties
uygulamasında bulunan özelliklerin okunması için gerekebilecek ortak kodu kaldırır. Böylece uygulamanız genelinde erişilebilir hale gelir.
Google Haritalar'a bağımlılık ekleyin
- Artık API anahtarınıza uygulamanın içinden erişilebilir. Sonraki adım, Android için Haritalar SDK'sının uygulamanızın
build.gradle
dosyasına eklenmesidir.
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, daha önceki bir adımda oluşturduğunuz API anahtarını iletmek için
AndroidManifest.xml
öğesine yeni birmeta-data
etiketi ekleyin. Bunu yapmak için bu dosyayı Android Studio'da açın veapp/src/main
dosyasında bulunanAndroidManifest.xml
dosyanızdakiapplication
nesnesinin içine şumeta-data
etiketini ekleyin.
AndroidManifest.xml
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${GOOGLE_MAPS_API_KEY}" />
- Daha sonra,
app/src/main/res/layout/
dizinindeactivity_main.xml
adlı yeni bir düzen dosyası oluşturun ve şu şekilde tanımlayın:
activity_main.xml (etkinlik)
<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
bulunur. Bu parça, daha sonraki adımlarda kullandığınız temel GoogleMaps
nesnesini içerir.
- Son olarak,
app/src/main/java/com/google/codelabs/buildyourfirstmap
yöntemini içerenMainActivity
sınıfını,onCreate
yöntemini geçersiz kılmak için aşağıdaki kodu ekleyerek oluşturun. Böylece, içeriğini yeni oluşturduğunuz düzenle ayarlayabilirsiniz.
Ana Etkinlik
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
- Şimdi uygulamayı çalıştırabilirsiniz. Artık harita yükünü cihazınızın ekranında görebilirsiniz.
5. Bulut tabanlı harita stili (İsteğe bağlı)
Bulut tabanlı harita stili kullanarak haritanızın stilini özelleştirebilirsiniz.
Harita kimliği oluşturma
İlişkilendirilmiş harita stiliyle henüz 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.
- Bir harita kimliğini bir harita stiliyle ilişkilendirin.
Harita kimliğini uygulamanıza 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 (etkinlik)
<fragment xmlns:map="http://schemas.android.com/apk/res-auto"
class="com.google.android.gms.maps.SupportMapFragment"
<!-- ... -->
map:mapId="YOUR_MAP_ID" />
Bunu tamamladıktan sonra devam edin ve haritanızı seçtiğiniz stilde görmek için uygulamayı çalıştırın.
6. İşaretçi ekle
Bu görevde, haritada vurgulamak istediğiniz önemli yerleri temsil eden işaretçiler haritaya eklersiniz. Öncelikle sizin için başlangıç projesinde sağlanan yerlerin listesini aldıktan sonra bu yerleri haritaya eklersiniz. Bu örnekte bunlar bisiklet mağazalarıdır.
GoogleMap için referans alın
Öncelikle, yöntemlerini kullanabilmek için GoogleMap
nesnesine referans almanız gerekir. Bunu yapmak için setContentView()
çağrısından hemen sonra MainActivity.onCreate()
yönteminize aşağıdaki kodu ekleyin:
AnaEtkinlik.onCreate()
val mapFragment = supportFragmentManager.findFragmentById(
R.id.map_fragment
) as? SupportMapFragment
mapFragment?.getMapAsync { googleMap ->
addMarkers(googleMap)
}
Uygulama, ilk olarak SupportFragmentManager
nesnesinde findFragmentById()
yöntemini kullanarak önceki adımda eklediğiniz SupportMapFragment
öğesini bulur. Bir referans alındıktan sonra, getMapAsync()
çağrısı çağrılır ve ardından bir lambda iletilir. Bu lambda, GoogleMap
nesnesinin iletildiği yerdir. Bu lambdanın içinde, kısa bir süre içinde tanımlanan addMarkers()
yöntem çağrısı çağrılır.
Sağlanan sınıf: PlacesReader
Başlangıç projesinde PlacesReader
sınıfı size sunulmuştur. Bu sınıfta, places.json
adlı bir JSON dosyasında depolanan 49 yerin listesini okur ve bunları List<Place>
olarak döndürür. Bu yerler, San Francisco, CA, ABD çevresindeki bisiklet mağazalarının listesini temsil eder.
Bu sınıfın uygulanmasını merak ediyorsanız GitHub'da derse erişebilir veya PlacesReader
sınıfını Android Studio'da açabilirsiniz.
Yerler Okuyucu
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()
}
}
Yer yükle
Bisiklet mağazalarının listesini yüklemek için MainActivity
varlığına places
adlı bir mülk ekleyin ve aşağıdaki şekilde tanımlayın:
AnaEtkinlik.places
private val places: List<Place> by lazy {
PlacesReader(this).read()
}
Bu kod, List<Place>
içinde PlacesReader
döndüren read()
yöntemini çağırır. Bir Place
, name
adlı bir özelliğe, yer adına ve bir latLng
'ye (yerin bulunduğu koordinatlar) sahiptir.
Yer
data class Place(
val name: String,
val latLng: LatLng,
val address: LatLng,
val rating: Float
)
Eşlemek için işaretçiler ekleyin
Yer listesi belleğe yüklendiğine göre şimdi bu yerleri haritada temsil edebilirsiniz.
MainActivity
ürünündeaddMarkers()
adlı bir yöntem oluşturun ve aşağıdaki şekilde tanımlayın:
AnaEtkinlik.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
listesini tarar ve ardından, sağlanan GoogleMap
nesnesinde addMarker()
yöntemi çağrılır. İşaretçi, işaretçinin kendisini özelleştirmenize olanak tanıyan bir MarkerOptions
nesnesi örneklenerek oluşturulur. Bu durumda, işaretçinin başlığı ve konumu sağlanarak sırasıyla bisiklet mağazası adını ve koordinatlarını temsil eder.
- Şimdi uygulamayı çalıştırın ve az önce eklediğiniz işaretçileri görmek için San Francisco'ya gidin.
7. İşaretçileri özelleştir
Az önce eklediğiniz işaretçilerin, öne çıkmalarına ve kullanıcılara yararlı bilgiler iletmelerine yardımcı olacak çeşitli özelleştirme seçenekleri vardır. Bu görevde, her bir işaretçinin resmini ve bir işaretçiye dokunulduğunda görüntülenen bilgi penceresini özelleştirerek bunların bazılarını keşfedeceksiniz.
Bilgi penceresi ekleme
Varsayılan olarak, bir işaretçiye dokunduğunuzda bilgi penceresi başlığı ve snippet'ini (ayarlanmışsa) görüntüler. Bunu, yerin adresi ve puanı gibi ek bilgileri gösterecek şekilde özelleştirirsiniz.
mark_info_contents.xml oluşturun
Önce 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 > Düzen Kaynağı Dosyası'nı seçin.
- İletişim kutusunda Dosya adı alanına
marker_info_contents
veRoot element
alanınaLinearLayout
yazın, ardından Tamam'ı tıklayın.
Bu düzen dosyası, daha sonra bilgi penceresinde içeriği temsil edecek şekilde şişirilir.
- Aşağıdaki kod snippet'inde içerikleri kopyalayın. Dikey
LinearLayout
görünüm grubuna üçTextViews
ekleyin ve dosyadaki varsayılan kodun üzerine yazın.
mark_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 uygulaması oluşturma
Özel bilgi penceresi için düzen dosyası oluşturduktan sonra, sıradaki adım GoogleMap.InfowindowAdapter arayüzünü uygulamaktır. Bu arayüz iki yöntem içeriyor: getInfoWindow()
ve getInfoContents()
. Her iki yöntem de isteğe bağlı bir View
nesnesi döndürür. Bunlardan ilki, pencerenin kendisini özelleştirmek için, diğeri de içeriğini özelleştirmek için kullanılır. Bu durumda, ikisini de uygular ve getInfoWindow()
içinde null (döndürücü) döndürürken getInfoContents()
dönüşünü özelleştirirsiniz. Bu, varsayılan pencerenin kullanılması gerektiğini belirtir.
- 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 kutusuna
MarkerInfoWindowAdapter
yazın ve Dosya'yı vurgulanmış olarak tutun.
- Dosyayı oluşturduktan sonra, aşağıdaki kod snippet'indeki içeriği 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çeriğinde, yöntemde sağlanan İşaretçi bir Place
türüne yayınlanır ve yayınlama mümkün değilse yöntem sıfıra döner (henüz Marker
öğesinde etiket özelliğini ayarlamadınız ancak bunu sonraki adımda yapabilirsiniz).
Ardından, marker_info_contents.xml
düzeni şişirilir ve TextViews
değerini içeren metin Place
etiketine ayarlanır.
MainActivity'yi güncelleme
Şu ana kadar oluşturduğunuz tüm bileşenleri yapıştırmak için MainActivity
sınıfınıza iki satır eklemeniz gerekir.
İlk olarak, getMapAsync
yöntemi çağrısında özel InfoWindowAdapter
, MarkerInfoWindowAdapter
öğesini iletmek için GoogleMap
nesnesinde setInfoWindowAdapter()
yöntemini çağırın ve yeni bir MarkerInfoWindowAdapter
örneği oluşturun.
- Bunu,
getMapAsync()
lambda içindekiaddMarkers()
yöntem çağrısından sonra aşağıdaki kodu ekleyerek yapabilirsiniz.
AnaEtkinlik.onCreate()
// Set custom info window adapter
googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
Son olarak, her Yeri haritaya eklenen her İşaretçide etiket özelliği olarak ayarlamanız gerekir.
- Bunu yapmak için
addMarkers()
işlevindekiplaces.forEach{}
çağrısını aşağıdaki şekilde değiştirin:
AnaEtkinlik.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 yerin türünü bildirmenin 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österirsiniz. Başlangıç projesi, app/src/res/drawable
içinde kullandığınız bisiklet simgesini ic_directions_bike_black_24dp.xml
içeriyor.
İşaretçide özel bit eşlem ayarla
Elinizdeki vektör çekilebilir bisiklet simgesi hazır olduğunda, bir sonraki adım, harita üzerindeki her bir işaretleyiciyi çekilebilir hale getirmektir. MarkerOptions
, bunu yapmak için kullandığınız bir BitmapDescriptor
olan icon
yöntemine sahiptir.
Önce, az önce eklediğiniz vektörü BitmapDescriptor
değerine dönüştürmeniz gerekir. Başlangıç projesinin içerdiği BitMapHelper
adlı bir dosya, vectorToBitmap()
adında bir yardımcı işlevi içerir.
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, bir Context
, çekilebilir kaynak kimliği ve bir renk tam sayısı alır ve bunun bir BitmapDescriptor
temsilini oluşturur.
Yardımcı yöntemi kullanarak, bicycleIcon
adlı yeni bir özellik bildirip ş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 renklendirmek ve BitmapDescriptor
olarak döndürmek için bunu kullanır.
- Bu özelliği kullanarak simge özelleştirmenizi tamamlamak için
addMarkers()
yöntemini kullanarakicon
MarkerOptions
yöntemini çağırın. Bunu yaptığınızda, işaretçi özelliği aşağıdaki gibi görünecektir:
AnaEtkinlik.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çilerin etkileşim kurması ve çok fazla gürültü oluşturması çok zordur. Bu da uygulamanızın kullanılabilirliğini etkiler.
Bunun için kullanıcı deneyimini iyileştirmek amacıyla, yakından kümelenen büyük bir veri kümeniz olduğunda en iyi uygulama, işaretçi kümelemeyi uygulamaktır. Kümelemede, haritayı yakınlaştırdığınızda ve uzaklaştırdığınızda, yakın mesafedeki işaretçiler aşağıdaki gibi birleştirilir:
Bunu uygulamak için Android Yardımcı Kitaplık Kitaplığı'ndan yardım almanız gerekir.
Android Yardımcı Program Kitaplığı için Haritalar SDK'sı
Android için Haritalar SDK'sı, Android için Haritalar SDK'sının işlevselliğini genişletmenin bir yolu olarak oluşturuldu. İşaretçi kümeleme, ısı haritaları, KML ve GeoJson desteği, çoklu çizgi kodlama ve kod çözme gibi gelişmiş özellikler ve küresel geometri etrafında birkaç yardımcı işlev sunar.
build.gradle dosyanızı güncelleme
Hizmet kitaplığı Android için Haritalar SDK'sından ayrı olarak paketlendiğinden build.gradle
dosyanıza ek bir bağımlılık eklemeniz gerekir.
- Şimdi
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 bir proje senkronizasyonu gerçekleştirmeniz gerekir.
Kümelemeyi uygulama
Uygulamanızda kümeleme uygulamak için şu üç adımı uygulayın:
ClusterItem
arayüzünü uygulayın.DefaultClusterRenderer
sınıfında alt sınıf oluşturun.ClusterManager
oluşturun ve öğe ekleyin.
ClusterItem arayüzünü uygulama
Haritadaki kümelenebilir işaretçiyi temsil eden tüm nesneler ClusterItem
arayüzünü uygulamalıdır. Bu durumda, Place
modelinin ClusterItem
ile uyumlu olması gerekir. Şimdi Place.kt
dosyasını açıp üzerinde aşağıdaki değişiklikleri yapın:
Yer
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 aşağıdaki üç yöntemi tanımlar:
- Yerin
LatLng
temsil edengetPosition()
. - Yerin adını temsil eden
getTitle()
- Yerin adresini temsil eden
getSnippet()
.
DefaultClusterRenderer sınıfının alt sınıfı
Kümeleri uygulamaktan sorumlu sınıf (ClusterManager
), haritayı kaydırırken ve yakınlaştırırken küme oluşturma işlemini işlemek için dahili olarak bir ClusterRenderer
sınıfı kullanır. Varsayılan olarak, ClusterRenderer
uygulayan varsayılan oluşturucu olan DefaultClusterRenderer
ile birlikte gelir. Basit durumlarda bu yeterlidir. Ancak sizin durumunuzda, işaretçilerin özelleştirilmesi gerektiğinden bu sınıfı genişletmeniz ve özelleştirmeleri buraya eklemeniz gerekir.
Şimdi com.google.codelabs.buildyourfirstmap.place
paketindeki PlaceRenderer.kt
Kotlin dosyasını oluşturup şu şekilde 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
üzerinden özelleştirmeler sağlayabilirsiniz. Bu durumda, işaretçinin başlığını, konumunu ve simgesini ayarlarsınız.- İşaretçi, haritada oluşturulduktan hemen sonra çağrılan
onClusterItemRenderer()
. Burada, oluşturulanMarker
nesnesine erişebilirsiniz. Bu durumda, işaretçinin etiket özelliği ayarlanır.
ClusterManager oluşturma ve öğe ekleme
Son olarak, kümenin çalışması için MainActivity
öğesini bir ClusterManager
örneklendirecek ve ona bağımlı olacak şekilde değiştirmeniz gerekir. ClusterManager
, işaretçileri (ClusterItem
nesnesi) dahili olarak ekler. Bu nedenle, işaretçileri doğrudan haritaya eklemek yerine bu sorumluluk ClusterManager
adlı kişiye aittir. Ayrıca ClusterManager
, setInfoWindowAdapter()
içindeki öğeleri de çağırır. Bu nedenle, ClusterManger
&MarkerManager.Collection
nesnesinde özel bir bilgi penceresi ayarlanması gerekir.
- Başlamak için,
MainActivity.onCreate()
içindegetMapAsync()
görüşmesindeki lambdanın içeriğini değiştirin. Devam edipaddMarkers()
vesetInfoWindowAdapter()
çağrısına yorum yapın ve bunun yerine,addClusteredMarkers()
adlı bir yöntem tanımlayın.
AnaEtkinlik.onCreate()
mapFragment?.getMapAsync { googleMap ->
//addMarkers(googleMap)
addClusteredMarkers(googleMap)
// Set custom info window adapter.
// googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
}
- Sonra,
MainActivity
dilindeaddClusteredMarkers()
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 bir ClusterManager
örneği oluşturur, PlacesRenderer
özel oluşturucusunu iletir, tüm yerleri ekler ve cluster()
yöntemini çağırır. Ayrıca, ClusterManager
, harita nesnesinde setInfoWindowAdapter()
yöntemini kullandığından özel bilgi penceresi ayarlamanın ClusterManager.markerCollection
nesnesinde yapılması gerekir. Son olarak, kullanıcı haritayı kaydırırken ve yakınlaştırırken kümeleme ayarının değiştirilmesini istediğiniz için googleMap
cihazına OnCameraIdleListener
sağlanır. Böylece, kamera boşta kaldığında clusterManager.onCameraIdle()
çağrılır.
- Gruplandırılmış yeni mağazaları görmek için uygulamayı çalıştırın.
9. Harita üzerinde çizin
Haritada çizim yapmanın bir yolunu daha önce keşfetmiş olsanız da (işaretçiler ekleyerek), Android için Haritalar SDK'sı, haritada yararlı bilgiler görüntülemek üzere çizebileceğiniz diğer birçok yöntemi destekler.
Örneğin, haritada rotaları ve alanları temsil etmek isterseniz bunları harita üzerinde göstermek için poligon ve poligonlar kullanabilirsiniz. Bir resmi zemin yüzeyine düzeltmek istiyorsanız zemin yer paylaşımları kullanabilirsiniz.
Bu görevde, işaretçiye her dokunulduğunda şeklin, özellikle bir dairenin nasıl çizileceğini öğrenirsiniz.
Tıklama işleyici ekleme
Genellikle bir işaretçiye tıklama işleyici eklemenin yolu, doğrudan GoogleMap
nesnesine setOnMarkerClickListener()
aracılığıyla bir tıklama işleyici iletmektir. Ancak kümeleme kullandığınız için bunun yerine tıklama işleyicinin ClusterManager
öğesine sağlanması gerekir.
MainActivity
ürünündeaddClusteredMarkers()
yönteminde, devam etmeden önce aşağıdaki satırıcluster()
adresine ekleyin.
MainActivity.addClusteredMarkers()
// Show polygon
clusterManager.setOnClusterItemClickListener { item ->
addCircle(googleMap, item)
return@setOnClusterItemClickListener false
}
Bu yöntem, bir işleyici ekler ve daha sonra tanımladığınız addCircle()
yöntemini çağırır. Son olarak, false
bu yöntemin bu etkinliği kullanmadığını belirtmek için bu yöntemden döndürülür.
- Ardından,
MainActivity
özelliğini kullanarakcircle
özelliğini veaddCircle()
yöntemini tanımlamanız gerekir.
AnaEtkinlik.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ılacağı ve yeni bir dairenin ekleneceği şekilde ayarlanır. Daire ekleme API'sının işaretçi eklemeye çok benzer olduğuna dikkat edin.
- Şimdi değişiklikleri görmek için uygulamayı çalıştırın.
10. Kamera Kontrolü
Son göreviniz olarak, görünümü belirli bir bölgeye odaklamak için bazı kamera kontrollerini kullanırsınız.
Kamera ve görünüm
Uygulamayı çalıştırdığınızda dikkatinizi çekerek kameranın Afrika kıtasını görüntülemesini ve eklediğiniz işaretçileri bulmak için San Francisco'da titizlikle kaydırmanız ve yakınlaştırmanız gerekir. Dünyayı keşfetmenin eğlenceli bir yolu olsa da, işaretçileri hemen göstermek istiyorsanız yararlı olmaz.
Bu konuda yardımcı olmak için, kameranın konumunu programlı bir şekilde ayarlayarak görünümün istediğiniz yere ortalanmasını sağlayabilirsiniz.
- Şimdi kamera uygulamasını
getMapAsync()
başlatıldıktan sonra uygulama başlatıldığında San Francisco'da başlatılacak şekilde ayarlamak için aşağıdaki kodu ekleyin.
AnaEtkinlik.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))
}
}
Önce setOnMapLoadedCallback()
, kamera güncellemesinin yalnızca harita yüklendikten sonra gerçekleştirilmesi için çağrılır. Kamera güncellemesi çağrısı yapmadan önce boyutlar gibi harita özelliklerinin hesaplanması gerektiğinden bu adım gereklidir.
Lambda'da, harita üzerinde dikdörtgen bir bölgeyi tanımlayan yeni bir LatLngBounds
nesnesi oluşturulur. Bu yöntem, tüm yerlerin sınırın içinde olmasını sağlamak için yere LatLng
değerlerini ekleyerek kademeli olarak oluşturulur. Bu nesne oluşturulduktan sonra GoogleMap
üzerindeki moveCamera()
yöntemi çağrılır ve CameraUpdateFactory.newLatLngBounds(bounds.build(), 20)
üzerinden buna bir CameraUpdate
sağlanır.
- Uygulamayı çalıştırın ve kameranın artık San Francisco'da başlatıldığına dikkat edin.
Kamera değişikliklerini dinleme
Kamera konumunu değiştirmenin yanı sıra kullanıcı harita üzerinde gezinirken kamera güncellemelerini de dinleyebilirsiniz. Bu, kamera hareket ederken kullanıcı arayüzünü değiştirmek isterseniz yararlı olabilir.
Sadece eğlence için, kodu değiştirdiğinizde işaretçiler şeffaf olacak şekilde değişir.
addClusteredMarkers()
yönteminde, devam edip yöntemin alt kısmı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 işlem, kamera hareket etmeye başladığında tüm işaretçilerin (kümeler ve işaretçiler) alfa değerlerinin 0.3f
olarak değiştirilmesini sağlayarak işaretçilerin yarı saydam olması için bir OnCameraMoveStartedListener
ekler.
- Son olarak, kamera durduğunda yarı saydam işaretçileri tekrar opak hale getirmek için
addClusteredMarkers()
yöntemindesetOnCameraIdleListener
içeriğini aşağıdaki şekilde 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()
}
- Şimdi sonuçları görmek için uygulamayı çalıştırın.
11. Haritalar KTX
Bir veya daha fazla Google Haritalar Platformu Android SDK'sı kullanan Kotlin uygulamaları için Kotlin uzantısı veya KTX kitaplıkları, eş yordam, uzantı özellikleri/işlevleri gibi Kotlin dili özelliklerinden yararlanmanızı sağlar. Her Google Haritalar SDK'sı, aşağıda gösterildiği gibi ilgili bir KTX kitaplığına sahiptir:
Bu görevde uygulamanız için Haritalar KTX ve Haritalar Utils KTX kitaplıklarını kullanacak ve Kotlin'e özgü dil özelliklerini uygulamanızda kullanabilmek için önceki görevlerin uygulamalarını yeniden düzenleyeceksiniz.
- Uygulama düzeyindeki build.gradle dosyanıza KTX bağımlılarını ekleyin
Uygulama hem Android için Haritalar SDK'sını hem de Android Yardımcı Program Kitaplığı'nın Haritalar SDK'sını kullandığından, bu kitaplıklar için ilgili KTX kitaplıklarını eklemeniz gerekir. Ayrıca, bu görevde AndroidX Lifecycle KTX kitaplığında bulunan bir özelliği de kullanacaksınız. Bu nedenle, 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 bir DSL stili API alternatifi sağlar. Yukarıda bahsedilen API'ları kullanmak için işaretçi veya daire seçenekleri içeren bir sınıfın oluşturulması gerekir. KTX alternatifleriyle, sağladığınız lambdada işaretçi veya daire seçeneklerini ayarlayabilirsiniz.
Bu API'leri kullanmak için MainActivity.addMarkers(GoogleMap)
ve MainActivity.addCircle(GoogleMap)
yöntemlerini güncelleyin:
MainActivity.addMarkers(GoogleHarita)
/**
* 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(GoogleHarita)
/**
* 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öntemlerin bu şekilde yeniden yazılması çok daha özlüdür. Bu nedenle, Kotlin’in alıcı işleviyle mümkün olmuştur.
- SupportMapFragment.arunMap() ve GoogleMap.apendingMapLoad() uzantı askıya alma işlevlerini kullanma
Maps KTX kitaplığı, eş yordamlarda kullanılmak üzere askıya alınan işlev uzantıları da sağlar. Özellikle SupportMapFragment.getMapAsync(OnMapReadyCallback)
ve GoogleMap.setOnMapLoadedCallback(OnMapLoadedCallback)
için askıya alınacak işlev alternatifleri mevcuttur. Bu alternatif API'leri kullanmak, geri çağırma isteğinin ortadan kalkmasını sağlar ve bu yöntemlerin yanıtını seri ve eşzamanlı şekilde almanızı sağlar.
Bu yöntemler işlevleri askıya aldığından, kullanımlarının eş yordam içinde gerçekleşmesi gerekir. Yaşam Döngüsü Çalışma Zamanı KTX kitaplığı, yaşam döngüsüne duyarlı eş yordam kapsamları sağlamak için bir uzantı sunar. Böylece kapsamlar, uygun yaşam döngüsü etkinliğinde çalıştırılıp durdurulur.
Bu kavramları bir araya getirerek MainActivity.onCreate(Bundle)
yöntemini güncelleyin:
MainActivity.onCreate(Paket)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mapFragment =
supportFragmentManager.findFragmentById(R.id.map_fragment) as SupportMapFragment
lifecycleScope.launchWhenCreated {
// Get map
val googleMap = mapFragment.awaitMap()
// Wait for map to finish loading
googleMap.awaitMapLoad()
// Ensure all places are visible in the map
val bounds = LatLngBounds.builder()
places.forEach { bounds.include(it.latLng) }
googleMap.moveCamera(CameraUpdateFactory.newLatLngBounds(bounds.build(), 20))
addClusteredMarkers(googleMap)
}
}
lifecycleScope.launchWhenCreated
eş yordamı kapsamı, etkinlik en azından oluşturulmuş durumda olduğunda engelleme işlemini yürütür. Ayrıca, GoogleMap
nesnesini alma ve haritanın yüklenmesinin tamamlanmasını beklemenin çağrılarının sırasıyla SupportMapFragment.awaitMap()
ve GoogleMap.awaitMapLoad()
ile değiştirildiğini unutmayın. Bu askıya alma işlevlerini kullanarak kodu yeniden düzenlemek, eşdeğer geri çağırmaya dayalı kodu sıralı bir şekilde yazmanıza olanak tanır.
- Devam edin ve yeniden yapılan değişikliklerinizle uygulamayı yeniden oluşturun!
12. Tebrikler
Tebrikler! Çok sayıda içeriği ele aldınız ve Android için Haritalar SDK'sında sunulan temel özellikleri daha iyi anladığınızı umuyoruz.
Daha fazla bilgi
- Android için Yerler SDK'sı - Çevrenizdeki işletmeleri keşfetmek için zengin yer verileri kümesini keşfedin.
- android-maps-ktx: Android için Haritalar SDK'sı ve Android için Yardımcı Program Kitaplığı'nın Kotlin dostu bir şekilde entegre edilmesini sağlayan açık kaynaklı bir kitaplık.
- android-place-ktx: Android için Yerler SDK'sıyla Kotlin dostu bir şekilde entegrasyon yapmanıza olanak tanıyan açık kaynak bir kitaplık.
- android-samples: Bu codelab'de yer alan tüm özellikleri gösteren GitHub'daki örnek kod ve daha fazlası.
- Google Haritalar Platformu ile Android uygulamaları geliştirmek için diğer Kotlin codelab'leri