1. قبل از شروع
این کد لبه به شما می آموزد که چگونه Maps SDK برای اندروید را با برنامه خود ادغام کنید و از ویژگی های اصلی آن با ساختن برنامه ای استفاده کنید که نقشه ای از فروشگاه های دوچرخه را در سانفرانسیسکو، کالیفرنیا، ایالات متحده نشان می دهد.
پیش نیازها
- دانش اولیه کاتلین و توسعه اندروید
کاری که خواهی کرد
- Maps SDK for Android را برای افزودن Google Maps به برنامه Android فعال کرده و از آن استفاده کنید.
- اضافه کردن، سفارشی کردن، و نشانگرهای خوشه.
- چند خطوط و چند ضلعی ها را روی نقشه بکشید.
- زاویه دید دوربین را به صورت برنامه ای کنترل کنید.
آنچه شما نیاز دارید
- Maps SDK برای اندروید
- یک حساب Google با فعال کردن صورتحساب
- Android Studio 2020.3.1 یا بالاتر
- خدمات Google Play در Android Studio نصب شده است
- یک دستگاه Android یا یک شبیهساز Android که پلتفرم Google APIs مبتنی بر Android نسخه 4.2.2 یا بالاتر را اجرا میکند (برای مراحل نصب به اجرای برنامهها در شبیهساز Android مراجعه کنید.)
2. راه اندازی شوید
برای مرحله فعال سازی زیر، باید Maps SDK برای Android را فعال کنید.
پلتفرم نقشه های گوگل را راه اندازی کنید
اگر قبلاً حساب Google Cloud Platform و پروژهای با صورتحساب فعال ندارید، لطفاً راهنمای شروع به کار با Google Maps Platform را برای ایجاد یک حساب صورتحساب و یک پروژه ببینید.
- در Cloud Console ، روی منوی کشویی پروژه کلیک کنید و پروژه ای را که می خواهید برای این کد لبه استفاده کنید انتخاب کنید.
- APIها و SDKهای پلتفرم Google Maps مورد نیاز برای این لبه کد را در Google Cloud Marketplace فعال کنید. برای انجام این کار، مراحل این ویدئو یا این مستند را دنبال کنید.
- یک کلید API در صفحه Credentials در Cloud Console ایجاد کنید. می توانید مراحل این ویدئو یا این مستند را دنبال کنید. همه درخواستها به پلتفرم نقشههای Google به یک کلید API نیاز دارند.
3. شروع سریع
برای شروع هر چه سریعتر، در اینجا چند کد شروع وجود دارد که به شما کمک میکند تا این نرمافزار را دنبال کنید. از شما استقبال می شود که به سراغ راه حل بروید، اما اگر می خواهید تمام مراحل ساخت آن را خودتان دنبال کنید، به خواندن ادامه دهید.
- اگر
git
را نصب کرده اید، مخزن را کلون کنید.
git clone https://github.com/googlecodelabs/maps-platform-101-android.git
همچنین میتوانید روی دکمه زیر کلیک کنید تا کد منبع را دانلود کنید.
- پس از دریافت کد، ادامه دهید و پروژه موجود در دایرکتوری
starter
را در Android Studio باز کنید.
4. نقشه های گوگل را اضافه کنید
در این بخش، نقشه های گوگل را اضافه می کنید تا با راه اندازی برنامه بارگذاری شود.
کلید API خود را اضافه کنید
کلید API که در مرحله قبل ایجاد کردید باید به برنامه ارائه شود تا Maps SDK برای Android بتواند کلید شما را با برنامه شما مرتبط کند.
- برای ارائه این، فایلی به نام
local.properties
را در فهرست اصلی پروژه خود باز کنید (همان سطحی کهgradle.properties
وsettings.gradle
هستند). - در آن فایل، یک کلید جدید
GOOGLE_MAPS_API_KEY
تعریف کنید که مقدار آن کلید API است که ایجاد کردهاید.
محلی.ملاک
GOOGLE_MAPS_API_KEY=YOUR_KEY_HERE
توجه داشته باشید که local.properties در فایل local.properties
در مخزن Git فهرست شده است .gitignore
این به این دلیل است که کلید API شما اطلاعات حساسی محسوب میشود و در صورت امکان نباید در کنترل منبع بررسی شود.
- در مرحله بعد، برای افشای API خود تا بتوان از آن در سراسر برنامه استفاده کرد، افزونه Secrets Gradle برای Android را در فایل
build.gradle
برنامه خود واقع در فهرستapp/
قرار دهید و خط زیر را در بلوکplugins
اضافه کنید:
build.gradle سطح برنامه
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
همچنین باید فایل build.gradle
در سطح پروژه خود را تغییر دهید تا مسیر کلاس زیر را شامل شود:
build.gradle در سطح پروژه
buildscript {
dependencies {
// ...
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:1.3.0"
}
}
این افزونه کلیدهایی را که در فایل local.properties
خود تعریف کرده اید به عنوان متغیرهای ساخت در فایل مانیفست اندروید و به عنوان متغیر در کلاس BuildConfig
تولید شده توسط Gradle در زمان ساخت در دسترس قرار می دهد. با استفاده از این افزونه، کد boilerplate که در غیر این صورت برای خواندن ویژگیها از local.properties
بود، حذف میشود تا بتوان به آن در سراسر برنامه دسترسی داشت.
وابستگی نقشه های گوگل را اضافه کنید
- اکنون که کلید API شما در داخل برنامه قابل دسترسی است، گام بعدی این است که وابستگی Maps SDK برای Android را به فایل
build.gradle
برنامه خود اضافه کنید.
در پروژه شروع که با این کد لبه ارائه می شود، این وابستگی قبلاً برای شما اضافه شده است.
build.gradle
dependencies {
// Dependency to include Maps SDK for Android
implementation 'com.google.android.gms:play-services-maps:17.0.0'
}
- در مرحله بعد، یک
meta-data
جدید درAndroidManifest.xml
اضافه کنید تا کلید API را که در مرحله قبل ایجاد کردهاید ارسال کنید. برای انجام این کار، ادامه دهید و این فایل را در Android Studio باز کنید و تگmeta-data
زیر را در داخل شیapplication
در فایلAndroidManifest.xml
خود که درapp/src/main
قرار دارد، اضافه کنید.
AndroidManifest.xml
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="${GOOGLE_MAPS_API_KEY}" />
- سپس یک فایل طرح بندی جدید به نام
activity_main.xml
در پوشهapp/src/main/res/layout/
ایجاد کنید و آن را به صورت زیر تعریف کنید:
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>
این طرح بندی دارای یک SupportMapFragment
FrameLayout
. این قطعه شامل شی GoogleMaps
زیرین است که در مراحل بعدی استفاده می کنید.
- در نهایت، کلاس
MainActivity
واقع درapp/src/main/java/com/google/codelabs/buildyourfirstmap
با اضافه کردن کد زیر برای لغو روشonCreate
بهروزرسانی کنید تا بتوانید محتوای آن را با طرحبندی جدیدی که ایجاد کردهاید تنظیم کنید.
فعالیت اصلی
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
- حالا ادامه دهید و برنامه را اجرا کنید. اکنون باید بارگذاری نقشه را روی صفحه دستگاه خود مشاهده کنید.
5. طراحی نقشه مبتنی بر ابر (اختیاری)
می توانید سبک نقشه خود را با استفاده از استایل نقشه مبتنی بر ابر سفارشی کنید.
یک شناسه نقشه ایجاد کنید
اگر هنوز شناسه نقشه با سبک نقشه مرتبط با آن ایجاد نکردهاید، برای تکمیل مراحل زیر، راهنمای Map IDs را ببینید:
- یک شناسه نقشه ایجاد کنید.
- شناسه نقشه را به سبک نقشه مرتبط کنید.
افزودن شناسه نقشه به برنامه شما
برای استفاده از شناسه نقشه ای که ایجاد کرده اید، فایل activity_main.xml
را تغییر دهید و شناسه نقشه خود را در ویژگی map:mapId
از SupportMapFragment
قرار دهید.
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" />
پس از تکمیل این کار، برنامه را اجرا کنید تا نقشه خود را به سبکی که انتخاب کرده اید ببینید!
6. نشانگرها را اضافه کنید
در این کار، نشانگرهایی را به نقشه اضافه می کنید که نشان دهنده نقاط مورد علاقه شما هستند که می خواهید روی نقشه برجسته شوند. ابتدا لیستی از مکان هایی که در پروژه شروع کننده برای شما ارائه شده است را بازیابی می کنید، سپس آن مکان ها را به نقشه اضافه می کنید. در این مثال، اینها مغازه های دوچرخه هستند.
یک مرجع به GoogleMap دریافت کنید
ابتدا باید یک مرجع به شی GoogleMap
بدست آورید تا بتوانید از روش های آن استفاده کنید. برای انجام این کار، کد زیر را در متد MainActivity.onCreate()
خود بلافاصله پس از فراخوانی setContentView()
اضافه کنید:
MainActivity.onCreate()
val mapFragment = supportFragmentManager.findFragmentById(
R.id.map_fragment
) as? SupportMapFragment
mapFragment?.getMapAsync { googleMap ->
addMarkers(googleMap)
}
پیاده سازی ابتدا SupportMapFragment
را که در مرحله قبل با استفاده از findFragmentById()
در شی SupportFragmentManager
اضافه کردید، پیدا می کند. هنگامی که یک مرجع به دست آمد، getMapAsync()
فراخوانی می شود و سپس در یک لامبدا ارسال می شود. این لامبدا جایی است که شی GoogleMap
می شود. در داخل این لامبدا، فراخوانی متد addMarkers()
فراخوانی می شود که به زودی تعریف می شود.
کلاس ارائه شده: PlacesReader
در پروژه استارتر کلاس PlacesReader
برای شما در نظر گرفته شده است. این کلاس لیستی از 49 مکان را می خواند که در یک فایل JSON به نام places.json
ذخیره شده اند و آنها را به عنوان List<Place>
برمی گرداند. این مکانها فهرستی از دوچرخهفروشیهای اطراف سانفرانسیسکو، کالیفرنیا، ایالات متحده را نشان میدهند.
اگر در مورد اجرای این کلاس کنجکاو هستید، می توانید در GitHub به آن دسترسی داشته باشید یا کلاس PlacesReader
را در Android Studio باز کنید.
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()
}
}
مکان ها را بارگیری کنید
برای بارگذاری لیست دوچرخهفروشیها، یک ویژگی به نام places
در MainActivity
اضافه کنید و آن را به صورت زیر تعریف کنید:
MainActivity.places
private val places: List<Place> by lazy {
PlacesReader(this).read()
}
این کد متد read()
را در PlacesReader
فراخوانی می کند که یک List<Place>
برمی گرداند. یک Place
دارای یک ویژگی به name
، نام مکان و یک latLng
- مختصاتی که مکان در آن قرار دارد.
محل
data class Place(
val name: String,
val latLng: LatLng,
val address: LatLng,
val rating: Float
)
نشانگرها را به نقشه اضافه کنید
اکنون که لیست مکان ها در حافظه بارگذاری شده است، مرحله بعدی نمایش این مکان ها روی نقشه است.
- یک متد در
MainActivity
به نامaddMarkers()
ایجاد کنید و آن را به صورت زیر تعریف کنید:
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)
)
}
}
این روش از طریق لیست places
و به دنبال آن فراخوانی addMarker()
در شیء ارائه شده GoogleMap
تکرار می شود. نشانگر با نمونه سازی یک شی MarkerOptions
ایجاد می شود که به شما امکان می دهد خود نشانگر را سفارشی کنید. در این حالت عنوان و موقعیت نشانگر ارائه می شود که به ترتیب نشان دهنده نام فروشگاه دوچرخه و مختصات آن است.
- ادامه دهید و برنامه را اجرا کنید و به سانفرانسیسکو بروید تا نشانگرهایی را که به تازگی اضافه کرده اید ببینید!
7. نشانگرها را سفارشی کنید
چندین گزینه سفارشیسازی برای نشانگرها وجود دارد که به تازگی اضافه کردهاید تا به آنها کمک کنید برجسته شوند و اطلاعات مفیدی را به کاربران منتقل کنند. در این کار، با سفارشی کردن تصویر هر نشانگر و همچنین پنجره اطلاعاتی که هنگام ضربه زدن روی یک نشانگر نمایش داده میشود، برخی از آنها را بررسی خواهید کرد.
افزودن پنجره اطلاعات
بهطور پیشفرض، وقتی روی نشانگر ضربه میزنید، پنجره اطلاعات عنوان و قطعه آن را نمایش میدهد (در صورت تنظیم). شما این را سفارشی می کنید تا بتواند اطلاعات اضافی مانند آدرس و رتبه مکان را نمایش دهد.
marker_info_contents.xml را ایجاد کنید
ابتدا یک فایل طرح بندی جدید به نام marker_info_contents.xml
ایجاد کنید.
- برای انجام این کار، روی پوشه
app/src/main/res/layout
در نمای پروژه در Android Studio کلیک راست کرده و New > Layout Resource File را انتخاب کنید.
- در کادر گفتگو،
marker_info_contents
را در قسمت File name وLinearLayout
را در قسمتRoot element
تایپ کنید، سپس روی OK کلیک کنید.
این فایل طرح بندی بعداً برای نمایش محتویات داخل پنجره اطلاعات باد می شود.
- محتویات قطعه کد زیر را کپی کنید، که سه
TextViews
در یک گروه نمایLinearLayout
عمودی اضافه می کند، و کد پیش فرض را در فایل بازنویسی کنید.
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 ایجاد کنید
پس از ایجاد فایل layout برای پنجره اطلاعات سفارشی، مرحله بعدی پیاده سازی رابط GoogleMap.InfoWindowAdapter است. این رابط شامل دو متد getInfoWindow()
و getInfoContents()
می باشد. هر دو روش یک شی View
اختیاری را برمیگردانند که در آن اولی برای سفارشی کردن خود پنجره استفاده میشود، در حالی که دومی برای سفارشی کردن محتویات آن است. در مورد شما، شما هر دو را پیادهسازی میکنید و بازگشت getInfoContents()
را سفارشی میکنید در حالی که در getInfoWindow()
null را برمیگردانید، که نشان میدهد باید از پنجره پیشفرض استفاده شود.
- با کلیک راست روی پوشه
app/src/main/java/com/google/codelabs/buildyourfirstmap
در نمای پروژه در Android Studio، یک فایل Kotlin جدید به نامMarkerInfoWindowAdapter
در همان بستهMainActivity
ایجاد کنید، سپس New > Kotlin File/Class را انتخاب کنید. .
- در گفتگو،
MarkerInfoWindowAdapter
را تایپ کنید و File را برجسته نگه دارید.
- پس از ایجاد فایل، محتوای موجود در قطعه کد زیر را در فایل جدید خود کپی کنید.
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()
، نشانگر ارائه شده در متد به نوع Place
فرستاده میشود و اگر امکان ارسال امکانپذیر نباشد، متد null را برمیگرداند (شما هنوز خاصیت برچسب را روی Marker
تنظیم نکردهاید، اما این کار را در مرحله بعد انجام دهید).
سپس، layout marker_info_contents.xml
باد میشود و سپس متن حاوی TextViews
را روی برچسب Place
تنظیم میکنیم.
MainActivity را به روز کنید
برای چسباندن تمام اجزایی که تاکنون ایجاد کرده اید، باید دو خط در کلاس MainActivity
خود اضافه کنید.
ابتدا، برای ارسال InfoWindowAdapter
سفارشی، MarkerInfoWindowAdapter
، در فراخوانی متد getMapAsync
، متد setInfoWindowAdapter()
را در شیء GoogleMap
فراخوانی کنید و یک نمونه جدید از MarkerInfoWindowAdapter
ایجاد کنید.
- این کار را با افزودن کد زیر پس از
addMarkers()
متد addMarkers در داخلgetMapAsync()
lambda انجام دهید.
MainActivity.onCreate()
// Set custom info window adapter
googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
در نهایت، باید هر مکان را به عنوان ویژگی برچسب روی هر نشانگری که به نقشه اضافه میشود، تنظیم کنید.
- برای انجام این کار،
places.forEach{}
را در تابعaddMarkers()
با موارد زیر تغییر دهید:
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
}
یک تصویر نشانگر سفارشی اضافه کنید
سفارشی کردن تصویر نشانگر یکی از راه های سرگرم کننده برای ارتباط با نوع مکانی است که نشانگر روی نقشه شما نشان می دهد. برای این مرحله، دوچرخهها را به جای نشانگرهای قرمز پیشفرض نمایش میدهید تا هر مغازه را روی نقشه نشان دهد. پروژه شروع کننده شامل نماد دوچرخه ic_directions_bike_black_24dp.xml
در app/src/res/drawable
است که شما از آن استفاده می کنید.
بیت مپ سفارشی را روی نشانگر تنظیم کنید
با در اختیار داشتن نماد دوچرخه قابل ترسیم برداری، گام بعدی این است که آن قابل ترسیم را به عنوان نماد هر نشانگر روی نقشه تنظیم کنید. MarkerOptions
یک icon
متد دارد که یک BitmapDescriptor
را که برای انجام این کار استفاده میکنید، وارد میکند.
ابتدا باید وکتور قابل ترسیمی را که به تازگی اضافه کرده اید به BitmapDescriptor
کنید. فایلی به نام BitMapHelper
که در پروژه شروع کننده گنجانده شده است حاوی یک تابع کمکی به نام vectorToBitmap()
است که دقیقاً این کار را انجام می دهد.
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)
}
}
این روش یک Context
، یک شناسه منبع قابل ترسیم و همچنین یک عدد صحیح رنگی را می گیرد و یک نمایش BitmapDescriptor
از آن ایجاد می کند.
با استفاده از متد helper، یک ویژگی جدید به نام bicycleIcon
کنید و تعریف زیر را به آن بدهید: 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)
}
این ویژگی از رنگ از پیش تعریف شده colorPrimary
در برنامه شما استفاده می کند و از آن برای رنگ آمیزی نماد دوچرخه و برگرداندن آن به عنوان BitmapDescriptor
استفاده می کند.
- با استفاده از این ویژگی، ادامه دهید و متد
icon
MarkerOptions
را درaddMarkers()
فراخوانی کنید تا سفارشی سازی آیکون خود را تکمیل کنید. با انجام این کار، ویژگی نشانگر باید به شکل زیر باشد:
MainActivity.addMarkers()
val marker = googleMap.addMarker(
MarkerOptions()
.title(place.name)
.position(place.latLng)
.icon(bicycleIcon)
)
- برنامه را اجرا کنید تا نشانگرهای به روز شده را ببینید!
8. نشانگرهای خوشه ای
بسته به اینکه چقدر روی نقشه زوم می کنید، ممکن است متوجه شده باشید که نشانگرهایی که اضافه کرده اید همپوشانی دارند. تعامل با نشانگرهای همپوشانی بسیار سخت است و نویز زیادی ایجاد می کند که بر قابلیت استفاده برنامه شما تأثیر می گذارد.
برای بهبود تجربه کاربر برای این کار، هر زمان که مجموعه داده بزرگی دارید که از نزدیک خوشهبندی شده است، بهترین روش اجرای خوشهبندی نشانگر است. با خوشهبندی، وقتی نقشه را بزرگنمایی و بزرگنمایی میکنید، نشانگرهایی که در مجاورت یکدیگر قرار دارند به این صورت در کنار هم قرار میگیرند:
برای اجرای این کار، به کمک Maps SDK for Android Utility Library نیاز دارید.
Maps SDK for Android Utility Library
Maps SDK for Android Utility Library به عنوان راهی برای گسترش عملکرد Maps SDK برای Android ایجاد شد. ویژگیهای پیشرفتهای مانند خوشهبندی نشانگر، نقشههای حرارتی، پشتیبانی KML و GeoJson، کدگذاری و رمزگشایی چند خطی، و تعداد کمی از توابع کمکی در اطراف هندسه کروی را ارائه میدهد.
build.gradle خود را به روز کنید
از آنجایی که کتابخانه ابزار به طور جداگانه از Maps SDK برای Android بسته بندی شده است، باید یک وابستگی اضافی به فایل build.gradle
خود اضافه کنید.
- ادامه دهید و بخش
dependencies
ها را در فایلapp/build.gradle
خود به روز کنید.
build.gradle
implementation 'com.google.maps.android:android-maps-utils:1.1.0'
- پس از افزودن این خط، باید همگام سازی پروژه را برای واکشی وابستگی های جدید انجام دهید.
اجرای خوشه بندی
برای پیاده سازی خوشه بندی در برنامه خود، این سه مرحله را دنبال کنید:
- رابط
ClusterItem
را پیاده سازی کنید. - کلاس
DefaultClusterRenderer
را زیر کلاس قرار دهید. - یک
ClusterManager
ایجاد کنید و موارد را اضافه کنید.
رابط ClusterItem را پیاده سازی کنید
تمام اشیایی که نشانگر خوشهای را روی نقشه نشان میدهند باید رابط ClusterItem
را پیادهسازی کنند. در مورد شما، این بدان معناست که مدل Place
باید با ClusterItem
مطابقت داشته باشد. ادامه دهید و فایل Place.kt
را باز کنید و تغییرات زیر را در آن انجام دهید:
محل
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 این سه روش را تعریف می کند:
-
getPosition()
که نشان دهندهLatLng
مکان است. -
getTitle()
که نشان دهنده نام مکان است -
getSnippet()
که نشان دهنده آدرس مکان است.
کلاس DefaultClusterRenderer را زیر کلاس قرار دهید
کلاسی که وظیفه اجرای خوشه بندی را بر عهده دارد، ClusterManager
، به صورت داخلی از یک کلاس ClusterRenderer
برای مدیریت ایجاد خوشه ها در حین حرکت و زوم در اطراف نقشه استفاده می کند. بهطور پیشفرض، رندر پیشفرض، DefaultClusterRenderer
، ارائه میشود که ClusterRenderer
را پیادهسازی میکند. برای موارد ساده، این باید کافی باشد. اما در مورد شما، چون نشانگرها باید سفارشی شوند، باید این کلاس را گسترش دهید و سفارشیسازیها را در آنجا اضافه کنید.
ادامه دهید و فایل Kotlin PlaceRenderer.kt
را در بسته com.google.codelabs.buildyourfirstmap.place
ایجاد کنید و آن را به صورت زیر تعریف کنید:
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
}
}
این کلاس این دو تابع را لغو می کند:
-
onBeforeClusterItemRendered()
که قبل از رندر شدن خوشه روی نقشه فراخوانی می شود. در اینجا، میتوانید سفارشیسازیها را از طریقMarkerOptions
ارائه دهید — در این مورد، عنوان، موقعیت و نماد نشانگر را تعیین میکند. -
onClusterItemRenderer()
که بلافاصله پس از رندر شدن نشانگر روی نقشه فراخوانی می شود. اینجاست که می توانید به شیء ایجاد شدهMarker
دسترسی داشته باشید—در این مورد، ویژگی تگ نشانگر را تنظیم می کند.
یک ClusterManager ایجاد کنید و موارد را اضافه کنید
در نهایت، برای اینکه خوشهبندی کار کند، باید MainActivity
را تغییر دهید تا یک ClusterManager
را نمونهسازی کنید و وابستگیهای لازم را برای آن فراهم کنید. ClusterManager
اضافه کردن نشانگرها (اشیاء ClusterItem
) را به صورت داخلی کنترل می کند، بنابراین به جای اضافه کردن نشانگرها به طور مستقیم بر روی نقشه، این مسئولیت به ClusterManager
محول می شود. علاوه بر این، ClusterManager
همچنین setInfoWindowAdapter()
را به صورت داخلی فراخوانی می کند، بنابراین تنظیم یک پنجره اطلاعات سفارشی باید روی شی MarkerManager.Collection
ClusterManger
انجام شود.
- برای شروع، محتویات لامبدا را در
getMapAsync()
درMainActivity.onCreate()
تغییر دهید. ادامه دهید و فراخوانی را باaddMarkers()
وsetInfoWindowAdapter()
، و به جای آن متدی به نامaddClusteredMarkers()
را فراخوانی کنید که در ادامه آن را تعریف می کنید.
MainActivity.onCreate()
mapFragment?.getMapAsync { googleMap ->
//addMarkers(googleMap)
addClusteredMarkers(googleMap)
// Set custom info window adapter.
// googleMap.setInfoWindowAdapter(MarkerInfoWindowAdapter(this))
}
- سپس، در
MainActivity
،addClusteredMarkers()
تعریف کنید.
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()
}
}
این متد یک ClusterManager
را نمونهسازی میکند، رندر سفارشی PlacesRenderer
را به آن ارسال میکند، همه مکانها را اضافه میکند و متد cluster()
را فراخوانی میکند. همچنین، از آنجایی که ClusterManager
از setInfoWindowAdapter()
روی شی نقشه استفاده می کند، تنظیم پنجره اطلاعات سفارشی باید روی شی ClusterManager.markerCollection
انجام شود. در نهایت، چون میخواهید هنگام حرکت کاربر و بزرگنمایی در اطراف نقشه، خوشهبندی تغییر کند، یک OnCameraIdleListener
برای googleMap
ارائه میشود، به طوری که وقتی دوربین بیکار میشود، clusterManager.onCameraIdle()
فراخوانی میشود.
- ادامه دهید و برنامه را اجرا کنید تا فروشگاه های خوشه ای جدید را ببینید!
9. روی نقشه بکشید
در حالی که قبلاً یک راه را برای ترسیم روی نقشه (با افزودن نشانگرها) بررسی کرده اید، Maps SDK برای Android از روش های متعدد دیگری برای نمایش اطلاعات مفید روی نقشه پشتیبانی می کند.
برای مثال، اگر میخواهید مسیرها و مناطق را روی نقشه نشان دهید، میتوانید از چند خط و چند ضلعی برای نمایش آنها روی نقشه استفاده کنید. یا اگر میخواهید تصویری را روی سطح زمین ثابت کنید، میتوانید از پوششهای زمینی استفاده کنید.
در این کار، یاد میگیرید که چگونه شکلها، بهویژه یک دایره را در اطراف یک نشانگر هر زمان که ضربه بزنید، بکشید.
شنونده کلیک را اضافه کنید
به طور معمول، راهی که میتوانید یک کلیک شنونده را به یک نشانگر اضافه کنید، ارسال یک کلیک شنونده به طور مستقیم روی شی GoogleMap
از طریق setOnMarkerClickListener()
است. با این حال، از آنجایی که شما از خوشه بندی استفاده می کنید، شنونده کلیک باید به ClusterManager
ارائه شود.
- در
addClusteredMarkers()
درMainActivity
، ادامه دهید و خط زیر را درست بعد از فراخوانی بهcluster()
اضافه کنید.
MainActivity.addClusteredMarkers()
// Show polygon
clusterManager.setOnClusterItemClickListener { item ->
addCircle(googleMap, item)
return@setOnClusterItemClickListener false
}
این متد یک شنونده اضافه می کند و متد addCircle()
را فراخوانی می کند که در ادامه آن را تعریف می کنید. در نهایت، false
از این روش برگردانده می شود تا نشان دهد که این روش این رویداد را مصرف نکرده است.
- در مرحله بعد، باید
circle
ویژگی و متدaddCircle()
را درMainActivity
تعریف کنید.
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
به گونه ای تنظیم شده است که هر زمان که روی نشانگر جدیدی ضربه می زند، دایره قبلی حذف شده و یک نشانگر جدید اضافه می شود. توجه داشته باشید که API برای افزودن یک دایره کاملاً شبیه به اضافه کردن یک نشانگر است.
- همین حالا بروید و برنامه را اجرا کنید تا تغییرات را ببینید.
10. کنترل دوربین
به عنوان آخرین کار خود، به برخی از کنترل های دوربین نگاه می کنید تا بتوانید نمای اطراف یک منطقه خاص را متمرکز کنید.
دوربین و نمای
اگر متوجه شدید که هنگام اجرای برنامه، دوربین قاره آفریقا را نمایش میدهد، و باید با زحمت به سانفرانسیسکو زوم کنید تا نشانگرهایی را که اضافه کردهاید پیدا کنید. در حالی که می تواند یک راه سرگرم کننده برای کاوش در جهان باشد، اگر بخواهید فورا نشانگرها را نمایش دهید مفید نیست.
برای کمک به این کار، میتوانید موقعیت دوربین را بهصورت برنامهای تنظیم کنید تا نما در مرکز آن جایی که میخواهید قرار گیرد.
- ادامه دهید و کد زیر را به
getMapAsync()
اضافه کنید تا نمای دوربین را به گونهای تنظیم کنید که هنگام راهاندازی برنامه به سانفرانسیسکو راهاندازی شود.
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))
}
}
ابتدا، setOnMapLoadedCallback()
فراخوانی می شود تا آپدیت دوربین فقط پس از بارگیری نقشه انجام شود. این مرحله ضروری است زیرا ویژگی های نقشه، مانند ابعاد، قبل از برقراری تماس به روز رسانی دوربین باید محاسبه شوند.
در لامبدا، یک شی LatLngBounds
جدید ساخته شده است که یک منطقه مستطیلی را روی نقشه تعریف می کند. این به طور تدریجی با گنجاندن تمام مقادیر LatLng
مکان در آن ساخته می شود تا اطمینان حاصل شود که همه مکان ها در داخل کران هستند. هنگامی که این شی ساخته شد، moveCamera()
در GoogleMap
فراخوانی می شود و CameraUpdate
از طریق CameraUpdateFactory.newLatLngBounds(bounds.build(), 20)
برای آن ارائه می شود.
- برنامه را اجرا کنید و متوجه شوید که دوربین اکنون در سانفرانسیسکو راه اندازی شده است.
گوش دادن به تغییرات دوربین
علاوه بر تغییر موقعیت دوربین، می توانید در حین حرکت کاربر در اطراف نقشه به به روز رسانی های دوربین نیز گوش دهید. این می تواند مفید باشد اگر می خواهید رابط کاربری را در حین حرکت دوربین تغییر دهید.
فقط برای سرگرمی، هر زمان که دوربین جابجا می شود، کد را طوری تغییر می دهید که نشانگرها شفاف شوند.
- در
addClusteredMarkers()
ادامه دهید و خطوط زیر را به انتهای متد اضافه کنید:
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 }
}
این یک OnCameraMoveStartedListener
اضافه می کند تا هر زمان که دوربین شروع به حرکت کرد، مقادیر آلفای تمام نشانگرها (هم خوشه ها و هم نشانگرها) به 0.3f
تغییر می یابد تا نشانگرها نیمه شفاف به نظر برسند.
- در نهایت، برای تغییر دادن نشانگرهای نیمه شفاف به حالت مات در هنگام توقف دوربین، محتویات
setOnCameraIdleListener
درaddClusteredMarkers()
را به صورت زیر تغییر دهید:
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()
}
- ادامه دهید و برنامه را اجرا کنید تا نتایج را ببینید!
11. نقشه های KTX
برای برنامههای Kotlin که از یک یا چند SDK Android پلتفرم نقشههای Google استفاده میکنند، افزونه Kotlin یا کتابخانههای KTX در دسترس هستند تا شما را قادر میسازند از ویژگیهای زبان Kotlin مانند برنامههای مشترک، ویژگیها/عملکردهای برنامه افزودنی و غیره استفاده کنید. هر Google Maps SDK دارای یک کتابخانه KTX مربوطه است که در زیر نشان داده شده است:
در این کار، از کتابخانه های Maps KTX و Maps Utils KTX برای برنامه خود استفاده می کنید و اجرای کارهای قبلی را اصلاح می کنید تا بتوانید از ویژگی های زبان مخصوص Kotlin در برنامه خود استفاده کنید.
- وابستگی های KTX را در فایل build.gradle در سطح برنامه خود قرار دهید
از آنجایی که برنامه از Maps SDK برای Android و Maps SDK for Android Utility Library استفاده میکند، باید کتابخانههای KTX مربوطه را برای این کتابخانهها اضافه کنید. شما همچنین از یک ویژگی موجود در کتابخانه AndroidX Lifecycle KTX در این کار استفاده خواهید کرد، بنابراین این وابستگی را نیز در فایل build.gradle
در سطح برنامه خود قرار دهید.
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() و GoogleMap.addCircle() استفاده کنید
کتابخانه Maps KTX یک جایگزین API به سبک DSL برای GoogleMap.addMarker(MarkerOptions)
و GoogleMap.addCircle(CircleOptions)
استفاده شده در مراحل قبلی ارائه میکند. برای استفاده از APIهای فوق الذکر، ساخت یک کلاس حاوی گزینه هایی برای نشانگر یا دایره ضروری است، در حالی که با جایگزین های KTX می توانید گزینه های نشانگر یا دایره را در لامبدای که ارائه می کنید، تنظیم کنید.
برای استفاده از این APIها، روشهای MainActivity.addMarkers(GoogleMap)
و MainActivity.addCircle(GoogleMap)
را بهروزرسانی کنید:
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))
}
}
بازنویسی روشهای فوق به این روش بسیار مختصرتر برای خواندن است که با استفاده از تابع Kotlin به معنای واقعی کلمه با گیرنده امکانپذیر است.
- از توابع تعلیق برنامه افزودنی SupportMapFragment.awaitMap () و GoogleMap.awaitMapLoad() استفاده کنید
کتابخانه Maps KTX همچنین پسوندهای تابع تعلیق را برای استفاده در کوروتین ها فراهم می کند. به طور خاص، گزینه های تعلیق تابع برای SupportMapFragment.getMapAsync(OnMapReadyCallback)
و GoogleMap.setOnMapLoadedCallback(OnMapLoadedCallback)
وجود دارد. استفاده از این APIهای جایگزین، نیاز به تماس های ارسالی را از بین می برد و در عوض به شما امکان می دهد پاسخ این روش ها را به صورت سریال و همزمان دریافت کنید.
از آنجایی که این روش ها توابع تعلیق هستند، استفاده از آنها باید در یک برنامه کاری انجام شود. کتابخانه Lifecycle Runtime KTX توسعهای را برای ارائه دامنههای کوروتین آگاه از چرخه حیات ارائه میکند تا برنامههای زمانی اجرا شوند و در رویداد چرخه حیات مناسب متوقف شوند.
با ترکیب این مفاهیم، روش MainActivity.onCreate(Bundle)
را به روز کنید:
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)
}
}
دامنه coroutine lifecycleScope.launchWhenCreated
زمانی بلوک را اجرا می کند که فعالیت حداقل در حالت ایجاد شده باشد. همچنین توجه داشته باشید که فراخوانهای بازیابی شی GoogleMap
و انتظار برای پایان بارگیری نقشه، به ترتیب با SupportMapFragment.awaitMap()
و GoogleMap.awaitMapLoad()
جایگزین شدهاند. فاکتورسازی مجدد کد با استفاده از این توابع تعلیق شما را قادر می سازد تا کد مبتنی بر تماس معادل را به صورت متوالی بنویسید.
- ادامه دهید و برنامه را با تغییرات بازسازی شده خود دوباره بسازید!
12. تبریک می گویم
تبریک می گویم! شما مطالب زیادی را پوشش دادید و امیدواریم درک بهتری از ویژگیهای اصلی ارائه شده در Maps SDK برای Android داشته باشید.
بیشتر بدانید
- مکانها SDK برای Android — مجموعه غنی از دادههای مکانها را برای کشف مشاغل اطراف خود کاوش کنید.
- android-maps-ktx — یک کتابخانه منبع باز که به شما امکان می دهد با Maps SDK برای اندروید و Maps SDK برای کتابخانه ابزار اندروید به روشی مناسب برای Kotlin ادغام شوید.
- android-place-ktx — یک کتابخانه منبع باز که به شما امکان می دهد با Places SDK برای اندروید به روشی مناسب برای Kotlin ادغام شوید.
- android-samples — کد نمونه در GitHub که تمام ویژگی های پوشش داده شده در این Codelab و موارد دیگر را نشان می دهد.
- کدهای Kotlin بیشتر برای ساخت برنامه های اندروید با پلتفرم نقشه های گوگل