با Places SDK برای Android (Kotlin) شروع کنید

1. قبل از شروع

این کد لبه به شما می آموزد که چگونه Places SDK برای اندروید را با برنامه خود ادغام کنید و از هر یک از ویژگی های Places SDK استفاده کنید.

برنامه نمایشی مکان ها

پیش نیازها

  • دانش اولیه کاتلین و توسعه اندروید

چیزی که یاد خواهید گرفت

  • نحوه نصب Places SDK برای اندروید با برنامه های افزودنی Kotlin.
  • نحوه بارگیری جزئیات مکان برای یک مکان خاص.
  • چگونه ویجت تکمیل خودکار مکان را به برنامه خود اضافه کنید.
  • نحوه بارگیری مکان کنونی بر اساس مکان گزارش شده فعلی دستگاه.

آنچه شما نیاز دارید

برای تکمیل این کد لبه، به حساب‌ها، خدمات و ابزارهای زیر نیاز دارید:

2. راه اندازی شوید

برای مرحله فعال سازی زیر، Places API و Maps SDK را برای Android فعال کنید.

پلتفرم نقشه های گوگل را راه اندازی کنید

اگر قبلاً یک حساب Google Cloud Platform و پروژه‌ای با صورت‌حساب فعال ندارید، لطفاً راهنمای شروع به کار با Google Maps Platform را برای ایجاد یک حساب صورت‌حساب و یک پروژه ببینید.

  1. در Cloud Console ، روی منوی کشویی پروژه کلیک کنید و پروژه ای را که می خواهید برای این کد لبه استفاده کنید انتخاب کنید.

  1. APIها و SDKهای پلتفرم Google Maps مورد نیاز برای این لبه کد را در Google Cloud Marketplace فعال کنید. برای انجام این کار، مراحل این ویدیو یا این مستند را دنبال کنید.
  2. یک کلید API در صفحه Credentials در Cloud Console ایجاد کنید. می توانید مراحل این ویدئو یا این مستند را دنبال کنید. همه درخواست‌ها به پلتفرم Google Maps به یک کلید API نیاز دارند.

3. شروع سریع

برای شروع هرچه سریع‌تر، کد شروع را دانلود کنید تا به شما کمک کند تا این نرم‌افزار را دنبال کنید. شما می توانید به سراغ راه حل بروید، اما اگر می خواهید تمام مراحل ساخت آن را خودتان دنبال کنید، به خواندن ادامه دهید.

  1. اگر git را نصب کرده اید، مخزن را کلون کنید.
git clone https://github.com/googlemaps/codelab-places-101-android-kotlin.git

یا برای دانلود کد منبع روی این دکمه کلیک کنید.

  1. پس از دانلود کد، پروژه ای را که در داخل پوشه /starter در اندروید استودیو یافت می شود، باز کنید. این پروژه شامل ساختار فایل اصلی است که برای تکمیل کد لبه نیاز دارید. هر چیزی که برای کار با آن نیاز دارید در پوشه /starter قرار دارد.

اگر می خواهید کد راه حل کامل را در حال اجرا ببینید، می توانید کد تکمیل شده را در پوشه /solution مشاهده کنید.

4. کلید API خود را به پروژه اضافه کنید

این بخش نحوه ذخیره کلید API خود را توضیح می دهد تا بتواند به طور ایمن توسط برنامه شما ارجاع داده شود. شما نباید کلید API خود را در سیستم کنترل نسخه خود بررسی کنید، بنابراین توصیه می کنیم آن را در فایل secrets.properties ذخیره کنید، که در کپی محلی شما از دایرکتوری ریشه پروژه شما قرار می گیرد. برای اطلاعات بیشتر در مورد فایل secrets.properties ، به فایل‌های خصوصیات Gradle مراجعه کنید.

برای ساده‌سازی این کار، توصیه می‌کنیم از افزونه Secrets Gradle برای اندروید استفاده کنید.

برای نصب افزونه Secrets Gradle برای اندروید در پروژه Google Maps:

  1. در Android Studio، فایل build.gradle.kts یا build.gradle سطح بالای خود را باز کنید و کد زیر را به عنصر dependencies در زیر buildscript اضافه کنید.

اگر از build.gradle.kts استفاده می کنید، اضافه کنید:

buildscript {
    dependencies {
        classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1")
    }
}

اگر از build.gradle استفاده می کنید، اضافه کنید:

buildscript {
    dependencies {
        classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
    }
}
  1. فایل build.gradle.kts یا build.gradle سطح ماژول خود را باز کنید و کد زیر را به عنصر plugins اضافه کنید.

اگر از build.gradle.kts استفاده می کنید، اضافه کنید:

plugins {
    // ...
    id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
}

اگر از build.gradle استفاده می کنید، اضافه کنید:

plugins {
    // ...
    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
  1. در فایل build.gradle.kts یا build.gradle در سطح ماژول، مطمئن شوید که targetSdk و compileSdk روی 34 تنظیم شده اند.
  2. فایل را ذخیره کنید و پروژه خود را با Gradle همگام کنید .
  3. فایل secrets.properties را در دایرکتوری سطح بالای خود باز کنید و سپس کد زیر را اضافه کنید. کلید API خود را جایگزین YOUR_API_KEY کنید. کلید خود را در این فایل ذخیره کنید زیرا secrets.properties از بررسی سیستم کنترل نسخه حذف شده است.
PLACES_API_KEY=YOUR_API_KEY
  1. فایل را ذخیره کنید.
  2. فایل local.defaults.properties را در پوشه سطح بالای خود، همان پوشه فایل secrets.properties ایجاد کنید و سپس کد زیر را اضافه کنید.
PLACES_API_KEY=DEFAULT_API_KEY

هدف این فایل ارائه یک مکان پشتیبان برای کلید API در صورت یافت نشدن فایل secrets.properties است تا بیلدها خراب نشوند. اگر برنامه را از یک سیستم کنترل نسخه که secrets.properties حذف می کند و هنوز فایل secrets.properties را به صورت محلی برای ارائه کلید API خود ایجاد نکرده اید، ممکن است اتفاق بیفتد.

  1. فایل را ذخیره کنید.
  2. در Android Studio، فایل build.gradle.kts یا build.gradle در سطح ماژول خود را باز کنید و ویژگی secrets را ویرایش کنید. اگر ویژگی secrets وجود ندارد، آن را اضافه کنید.

ویژگی های افزونه را ویرایش کنید تا propertiesFileName روی secrets.properties تنظیم کنید، defaultPropertiesFileName را روی local.defaults.properties تنظیم کنید و هر ویژگی دیگری را تنظیم کنید.

secrets {
    // Optionally specify a different file name containing your secrets.
    // The plugin defaults to "local.properties"
    propertiesFileName = "secrets.properties"

    // A properties file containing default secret values. This file can be
    // checked in version control.
    defaultPropertiesFileName = "local.defaults.properties"
}

5. Places SDK را برای اندروید نصب کنید

در این بخش، Places SDK برای اندروید را به وابستگی های برنامه خود اضافه می کنید.

  1. اکنون که کلید API شما در داخل برنامه قابل دسترسی است، وابستگی Places SDK for Android را به فایل build.gradle برنامه خود اضافه کنید.

فایل build.gradle در سطح برنامه خود را تغییر دهید تا وابستگی به Places SDK برای Android اضافه شود:

build.gradle در سطح برنامه

dependencies {
   // Dependency to include Places SDK for Android
   implementation 'com.google.android.libraries.places:places:3.4.0'
}
  1. برنامه را اجرا کنید.

اکنون باید یک برنامه با صفحه خالی را ببینید. به پر کردن این صفحه با سه نسخه نمایشی ادامه دهید.

6. Places Android KTX را نصب کنید

برای برنامه‌های Kotlin که از یک یا چند SDK Android پلتفرم نقشه‌های Google استفاده می‌کنند، کتابخانه‌های برنامه افزودنی Kotlin (KTX) به شما امکان می‌دهند از ویژگی‌های زبان Kotlin مانند برنامه‌های مشترک، ویژگی‌ها/توابع برنامه افزودنی و غیره استفاده کنید. هر Google Maps SDK دارای یک کتابخانه KTX مربوطه است که در زیر نشان داده شده است:

نمودار KTX پلتفرم نقشه های گوگل

در این کار، از کتابخانه Places Android KTX برای استفاده از ویژگی های زبان مخصوص Kotlin در برنامه خود استفاده کنید.

وابستگی Places Android KTX را اضافه کنید

برای بهره‌گیری از ویژگی‌های خاص Kotlin، کتابخانه KTX مربوطه را برای این SDK در فایل build.gradle در سطح برنامه خود قرار دهید.

build.gradle

dependencies {
    // ...

    // Places SDK for Android KTX Library
    implementation 'com.google.maps.android:places-ktx:3.1.1'
}

7. Places Client را راه اندازی کنید

Places SDK را برای محدوده برنامه راه اندازی کنید

در فایل DemoApplication.kt پوشه app/src/main/java/com/google/codelabs/maps/placesdemo ، Places SDK برای Android را مقداردهی اولیه کنید. خطوط زیر را در انتهای تابع onCreate قرار دهید:

        // Initialize the SDK with the Google Maps Platform API key
        Places.initialize(this, BuildConfig.PLACES_API_KEY)

وقتی برنامه خود را می‌سازید، افزونه Secrets Gradle برای Android، کلید API را در فایل secrets.properties شما به‌عنوان BuildConfig.PLACES_API_KEY در دسترس قرار می‌دهد.

فایل برنامه را به مانیفست اضافه کنید

از آنجایی که Application با DemoApplication گسترش داده اید، باید مانیفست را به روز کنید. ویژگی android:name را به عنصر application در فایل AndroidManifest.xml ، واقع در app/src/main اضافه کنید:

    <application
        android:name=".DemoApplication"
        ...
    </application>

این کد مانیفست برنامه را به کلاس DemoApplication در پوشه src/main/java/com/google/codelabs/maps/placesdemo/ هدایت می‌کند.

8. واکشی جزئیات مکان

یک صفحه جزئیات ایجاد کنید

یک طرح‌بندی activity_details.xml با یک LinearLayout خالی در پوشه app/src/main/res/layout/ موجود است. طرح خطی را با اضافه کردن کد زیر بین براکت های <LinearLayout> پر کنید.

    <com.google.android.material.textfield.TextInputLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.textfield.TextInputEditText
            android:id="@+id/details_input"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/details_input_hint"
            android:text="@string/details_input_default" />

    </com.google.android.material.textfield.TextInputLayout>

    <Button
        android:id="@+id/details_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/button_details" />

    <TextView
        android:id="@+id/details_response_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:textIsSelectable="true" />

این کد یک فیلد ورودی متن اضافه می کند که در آن کاربر می تواند هر شناسه مکان را وارد کند یا از پیش فرض ارائه شده استفاده کند، یک دکمه برای شروع درخواست جزئیات مکان، و یک TextView برای نمایش اطلاعات از پاسخ. رشته های مرتبط برای شما در فایل src/main/res/values/strings.xml تعریف شده است.

یک فعالیت Details ایجاد کنید

  1. یک فایل DetailsActivity.kt در پوشه src/main/java/com/google/codelabs/maps/placesdemo/ ایجاد کنید و آن را با طرح‌بندی که ایجاد کرده‌اید مرتبط کنید. این کد را در فایل قرار دهید:
@ExperimentalCoroutinesApi
class DetailsActivity : AppCompatActivity() {
    private lateinit var placesClient: PlacesClient
    private lateinit var detailsButton: Button
    private lateinit var detailsInput: TextInputEditText
    private lateinit var responseView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_details)

        // Set up view objects
        detailsInput = findViewById(R.id.details_input)
        detailsButton = findViewById(R.id.details_button)
        responseView = findViewById(R.id.details_response_content)

        val apiKey = BuildConfig.PLACES_API_KEY

        // Log an error if apiKey is not set.
        if (apiKey.isEmpty() || apiKey == "DEFAULT_API_KEY") {
            Log.e(TAG, "No api key")
            finish()
            return
        }
    }
}
  1. یک Places Client برای استفاده در این فعالیت ایجاد کنید. این کد را بعد از کد قرار دهید تا کلید API را در تابع onCreate بررسی کنید.
        // Retrieve a PlacesClient (previously initialized - see DemoApplication)
        placesClient = Places.createClient(this)
  1. پس از تنظیم Places Client، یک کلیک شنونده را به دکمه متصل کنید. این کد را بعد از ایجاد Places Client در تابع onCreate قرار دهید.
        // Upon button click, fetch and display the Place Details
        detailsButton.setOnClickListener { button ->
            button.isEnabled = false
            val placeId = detailsInput.text.toString()
            val placeFields = listOf(
                Place.Field.NAME,
                Place.Field.ID,
                Place.Field.LAT_LNG,
                Place.Field.ADDRESS
            )
            lifecycleScope.launch {
                try {
                    val response = placesClient.awaitFetchPlace(placeId, placeFields)
                    responseView.text = response.prettyPrint()
                } catch (e: Exception) {
                    e.printStackTrace()
                    responseView.text = e.message
                }
                button.isEnabled = true
            }
        }

این کد شناسه مکانی را که در فیلد ورودی وارد شده است بازیابی می‌کند، تعیین می‌کند کدام فیلدها را برای مکان درخواست کند، یک FetchPlaceRequest ایجاد می‌کند، کار را آغاز می‌کند و برای موفقیت یا شکست گوش می‌دهد. اگر درخواست موفقیت آمیز باشد، تابع TextView را با جزئیات درخواستی پر می کند.

فعالیت جزئیات را به مانیفست اضافه کنید

یک عنصر <activity> برای DetailsActivity به عنوان فرزند عنصر <application> در فایل AndroidManifest.xml ، واقع در app/src/main اضافه کنید:

        <activity android:name=".DetailsActivity" android:label="@string/details_demo_title" />

فعالیت جزئیات را به منوی نمایشی اضافه کنید

یک ماژول Demo خالی برای فهرست کردن دموهای موجود در صفحه اصلی ارائه شده است. اکنون که یک فعالیت جزئیات مکان ایجاد کردید، آن را با این کد به فایل Demo.kt در پوشه src/main/java/com/google/codelabs/maps/placesdemo/ اضافه کنید:

    DETAILS_FRAGMENT_DEMO(
        R.string.details_demo_title,
        R.string.details_demo_description,
        DetailsActivity::class.java
    ),

رشته های مرتبط در فایل src/main/res/values/strings.xml تعریف شده اند.

MainActivity.kt را بررسی کنید و مشاهده کنید که یک ListView ایجاد می کند که با تکرار در محتویات ماژول Demo پر می شود. اگر کاربر روی یک مورد در لیست ضربه بزند، شنونده کلیک فعالیت مرتبط را باز می کند.

برنامه را اجرا کنید

  1. برنامه را اجرا کنید. این بار باید یک مورد را در لیستی که نسخه نمایشی جزئیات مکان را ارائه می دهد، مشاهده کنید.
  2. روی متن جزئیات مکان ضربه بزنید. شما باید نمایی را که با یک فیلد ورودی و دکمه ایجاد کرده اید ببینید.
  3. روی دکمه «دریافت جزئیات» ضربه بزنید. اگر از شناسه مکان پیش‌فرض استفاده کرده‌اید، باید نام مکان، آدرس و مختصات نقشه را ببینید، همانطور که در شکل 1 نشان داده شده است.

فعالیت جزئیات مکان با پاسخ

شکل 1. فعالیت جزئیات مکان با نمایش پاسخ.

9. تکمیل خودکار مکان را اضافه کنید

یک صفحه تکمیل خودکار ایجاد کنید

یک طرح‌بندی activity_autocomplete.xml با یک LinearLayout خالی در پوشه app/src/main/res/layout/ ارائه شده است. طرح خطی را با اضافه کردن این کد بین براکت های <LinearLayout> پر کنید.

    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/autocomplete_fragment"
        android:background="@android:color/white"
        android:name="com.google.android.libraries.places.widget.AutocompleteSupportFragment"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <TextView
        android:id="@+id/autocomplete_response_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:textIsSelectable="true" />

این کد یک ویجت AutocompleteSupportFragment و یک TextView برای نمایش اطلاعات از پاسخ اضافه می کند. رشته های مرتبط در فایل src/main/res/values/strings.xml تعریف شده اند.

یک فعالیت تکمیل خودکار ایجاد کنید

  1. یک فایل AutocompleteActivity.kt در پوشه src/main/java/com/google/codelabs/maps/placesdemo/ ایجاد کنید و آن را با این کد تعریف کنید:
@ExperimentalCoroutinesApi
class AutocompleteActivity : AppCompatActivity() {
    private lateinit var responseView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_autocomplete)

        // Set up view objects
        responseView = findViewById(R.id.autocomplete_response_content)
        val autocompleteFragment =
            supportFragmentManager.findFragmentById(R.id.autocomplete_fragment)
                    as AutocompleteSupportFragment
    }
}

این کد فعالیت را با view ها و AutocompleteSupportFramgent که در فایل طرح بندی تعریف کرده اید مرتبط می کند.

  1. در مرحله بعد، مشخص کنید وقتی کاربر یکی از پیش بینی های ارائه شده توسط Place Autocomplete را انتخاب می کند چه اتفاقی می افتد. این کد را به انتهای تابع onCreate اضافه کنید:
        val placeFields: List<Place.Field> =
            listOf(Place.Field.NAME, Place.Field.ID, Place.Field.ADDRESS, Place.Field.LAT_LNG)
        autocompleteFragment.setPlaceFields(placeFields)

        // Listen to place selection events
        lifecycleScope.launchWhenCreated {
            autocompleteFragment.placeSelectionEvents().collect { event ->
                when (event) {
                    is PlaceSelectionSuccess -> {
                        val place = event.place
                        responseView.text = prettyPrintAutocompleteWidget(place, false)
                    }

                    is PlaceSelectionError -> Toast.makeText(
                        this@AutocompleteActivity,
                        "Failed to get place '${event.status.statusMessage}'",
                        Toast.LENGTH_SHORT
                    ).show()
                }
            }
        }

این کد مشخص می کند که کدام فیلدها را برای مکان درخواست کند، به رویداد انتخاب مکان گوش می دهد و برای موفقیت یا شکست گوش می دهد. اگر درخواست موفقیت آمیز باشد، تابع TextView را با جزئیات مکان پر می کند. توجه داشته باشید که Place Autocomplete یک شی Place را برمی گرداند. هنگام استفاده از ویجت تکمیل خودکار مکان، نیازی به درخواست جداگانه جزئیات مکان نیست.

فعالیت تکمیل خودکار را به مانیفست اضافه کنید

یک عنصر <activity> برای AutocompleteActivity به عنوان فرزند عنصر <application> در فایل AndroidManifest.xml ، واقع در app/src/main اضافه کنید:

        <activity android:name=".AutocompleteActivity" android:label="@string/autocomplete_fragment_demo_title" />

فعالیت تکمیل خودکار را به منوی نمایشی اضافه کنید

مانند قبل، نسخه ی نمایشی تکمیل خودکار مکان را با اضافه کردن آن به لیست در ماژول Demo ، به صفحه اصلی اضافه کنید. اکنون که یک فعالیت تکمیل خودکار مکان ایجاد کرده اید، آن را به فایل Demo.kt در پوشه src/main/java/com/google/codelabs/maps/placesdemo/ اضافه کنید. این کد را بلافاصله بعد از مورد DETAILS_FRAGMENT_DEMO جای‌گذاری کنید:

    AUTOCOMPLETE_FRAGMENT_DEMO(
        R.string.autocomplete_fragment_demo_title,
        R.string.autocomplete_fragment_demo_description,
        AutocompleteActivity::class.java
    ),

رشته های مرتبط در فایل src/main/res/values/strings.xml تعریف شده اند.

برنامه را اجرا کنید

  1. برنامه را اجرا کنید. این بار باید دو مورد را در لیست صفحه اصلی مشاهده کنید.
  2. روی ردیف تکمیل خودکار مکان ضربه بزنید. همانطور که در شکل 2 نشان داده شده است، باید یک ورودی تکمیل خودکار مکان ظاهر شود.
  3. شروع به تایپ نام یک مکان کنید. این می تواند نام مؤسسه، آدرس یا منطقه جغرافیایی باشد. پیش‌بینی‌ها باید همانطور که تایپ می‌کنید ارائه شوند.
  4. یکی از پیش بینی ها را انتخاب کنید. پیش بینی ها باید ناپدید شوند و TextView اکنون باید جزئیات مکان انتخاب شده را همانطور که در شکل 3 نشان داده شده است نشان دهد.

تکمیل خودکار فعالیت پس از ضربه زدن کاربر به قسمت ورودی

شکل 2. تکمیل خودکار فعالیت پس از ضربه زدن کاربر به قسمت ورودی.

تکمیل خودکار فعالیت پس از تایپ و انتخاب «آبشار نیاگارا» توسط کاربر

شکل 3. تکمیل خودکار فعالیت، نمایش جزئیات مکان پس از تایپ و انتخاب "آبشار نیاگارا" توسط کاربر.

10. مکان فعلی دستگاه را دریافت کنید

یک صفحه مکان فعلی ایجاد کنید

یک طرح‌بندی activity_current.xml با یک LinearLayout خالی در پوشه app/src/main/res/layout/ ارائه شده است. طرح خطی را با اضافه کردن کد زیر در بین براکت‌های <LinearLayout> پر کنید.

    <Button
        android:id="@+id/current_button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/current_button" />

    <TextView
        android:id="@+id/current_response_content"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:scrollbars = "vertical"
        android:textIsSelectable="true" />

یک فعالیت مکان فعلی ایجاد کنید

  1. یک فایل CurrentPlaceActivity.kt در پوشه src/main/java/com/google/codelabs/maps/placesdemo/ ایجاد کنید و آن را با این کد تعریف کنید:
@ExperimentalCoroutinesApi
class CurrentPlaceActivity : AppCompatActivity(), OnMapReadyCallback {
    private lateinit var placesClient: PlacesClient
    private lateinit var currentButton: Button
    private lateinit var responseView: TextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_current)

        // Retrieve a PlacesClient (previously initialized - see DemoApplication)
        placesClient = Places.createClient(this)

        // Set view objects
        currentButton = findViewById(R.id.current_button)
        responseView = findViewById(R.id.current_response_content)

        // Set listener for initiating Current Place
        currentButton.setOnClickListener {
            checkPermissionThenFindCurrentPlace()
        }
    }
}

این کد فعالیت را با نماهایی که در فایل layout تعریف کرده اید مرتبط می کند. همچنین یک کلیک شنونده به دکمه اضافه می کند تا با کلیک روی دکمه، تابع checkPermissionThenFindCurrentPlace فراخوانی کند.

  1. checkPermissionThenFindCurrentPlace() برای بررسی مجوز مکان دقیق و درخواست مجوز در صورتی که هنوز اعطا نشده است را تعریف کنید. این کد را بعد از تابع onCreate قرار دهید.
    /**
     * Checks that the user has granted permission for fine or coarse location.
     * If granted, finds current Place.
     * If not yet granted, launches the permission request.
     * See https://developer.android.com/training/permissions/requesting
     */
    private fun checkPermissionThenFindCurrentPlace() {
        when {
            (ContextCompat.checkSelfPermission(
                this,
                ACCESS_FINE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED || ContextCompat.checkSelfPermission(
                this,
                ACCESS_COARSE_LOCATION
            ) == PackageManager.PERMISSION_GRANTED) -> {
                // You can use the API that requires the permission.
                findCurrentPlace()
            }

            shouldShowRequestPermissionRationale(ACCESS_FINE_LOCATION)
            -> {
                Log.d(TAG, "Showing permission rationale dialog")
                // TODO: In an educational UI, explain to the user why your app requires this
                // permission for a specific feature to behave as expected. In this UI,
                // include a "cancel" or "no thanks" button that allows the user to
                // continue using your app without granting the permission.
            }

            else -> {
                // Ask for both the ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION permissions.
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(
                        ACCESS_FINE_LOCATION,
                        ACCESS_COARSE_LOCATION
                    ),
                    PERMISSION_REQUEST_CODE
                )
            }
        }
    }

    companion object {
        private const val TAG = "CurrentPlaceActivity"
        private const val PERMISSION_REQUEST_CODE = 9
    }
  1. هنگامی که شاخه else تابع checkPermissionThenFindCurrentPlace requestPermissions فرا می خواند، برنامه یک گفتگوی درخواست مجوز را به کاربر ارائه می دهد. اگر کاربر از دستگاهی استفاده می‌کند که دارای سیستم‌عامل پایین‌تر از Android 12 است، کاربر فقط می‌تواند مجوز مکان دقیق (خوب) بدهد. اگر کاربر از دستگاهی با اندروید 12 یا بالاتر استفاده می کند، همانطور که در شکل 4 نشان داده شده است، این گزینه به او داده می شود که مکان تقریبی (درشت) را به جای مکان دقیق (دقیق) ارائه دهد.

درخواست مجوز کاربر در دستگاهی که دارای Android 12 یا بالاتر است

شکل 4. درخواست مجوز کاربر در دستگاهی که دارای Android 12 یا بالاتر است، گزینه ای برای اعطای مکان دقیق یا تقریبی ارائه می دهد.

پس از پاسخ کاربر به گفتگوی مجوزهای سیستم، سیستم سپس اجرای برنامه شما از onRequestPermissionsResult را فراخوانی می کند. سیستم در پاسخ کاربر به گفتگوی مجوز و همچنین کد درخواستی که شما تعریف کرده اید ارسال می کند. onRequestPermissionResult لغو کنید تا با قرار دادن کد زیر در زیر checkPermissionThenFindCurrentPlace ، کد درخواست مجوزهای مکان مربوط به این فعالیت مکان فعلی را کنترل کنید.

    @SuppressLint("MissingPermission")
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<String>, grantResults: IntArray
    ) {
        if (requestCode != PERMISSION_REQUEST_CODE) {
            super.onRequestPermissionsResult(
                requestCode,
                permissions,
                grantResults
            )
            return
        } else if (
            permissions.toList().zip(grantResults.toList())
                .firstOrNull { (permission, grantResult) ->
                    grantResult == PackageManager.PERMISSION_GRANTED && (permission == ACCESS_FINE_LOCATION || permission == ACCESS_COARSE_LOCATION)
                } != null
        )
        // At least one location permission has been granted, so proceed with Find Current Place
        findCurrentPlace()
    }
  1. پس از اعطای مجوز، تابع findCurrentPlace اجرا خواهد شد. تابع را با این کد بعد از تابع onRequestPermissionsResult تعریف کنید.
    /**
     * Fetches a list of [PlaceLikelihood] instances that represent the Places the user is
     * most likely to be at currently.
     */
    @RequiresPermission(anyOf = [ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION])
    private fun findCurrentPlace() {
        // Use fields to define the data types to return.
        val placeFields: List<Place.Field> =
            listOf(Place.Field.NAME, Place.Field.ID, Place.Field.ADDRESS, Place.Field.LAT_LNG)

        // Call findCurrentPlace and handle the response (first check that the user has granted permission).
        if (ContextCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) ==
            PackageManager.PERMISSION_GRANTED ||
            ContextCompat.checkSelfPermission(this, ACCESS_COARSE_LOCATION) ==
            PackageManager.PERMISSION_GRANTED
        ) {
            // Retrieve likely places based on the device's current location
            currentButton.isEnabled = false
            lifecycleScope.launch {
                val response = placesClient.awaitFindCurrentPlace(placeFields)

                responseView.text = response.prettyPrint()

                // Enable scrolling on the long list of likely places
                val movementMethod = ScrollingMovementMethod()
                responseView.movementMethod = movementMethod
            }
        } else {
            Log.d(TAG, "LOCATION permission not granted")
            checkPermissionThenFindCurrentPlace()
        }
    }

این کد تعیین می کند که کدام فیلدها برای مکان های احتمالی درخواست شود، یک FindCurrentPlaceRequest ایجاد می کند، کار را آغاز می کند، و TextView را با جزئیات درخواستی پر می کند.

فعالیت Current Place را به مانیفست اضافه کنید

یک عنصر <activity> برای CurrentPlaceActivity به عنوان فرزند عنصر <application> در فایل AndroidManifest.xml ، واقع در app/src/main اضافه کنید:

        <activity android:name=".CurrentPlaceActivity" android:label="@string/current_demo_title" />

فعالیت Current Place را به منوی نمایشی اضافه کنید

دقیقاً مانند قبل، نسخه نمایشی Current Place را با افزودن آن به لیست در ماژول Demo به صفحه اصلی اضافه کنید. اکنون که یک فعالیت Current Place ایجاد کرده اید، آن را به فایل Demo.kt در پوشه src/main/java/com/google/codelabs/maps/placesdemo/ اضافه کنید. این کد را بلافاصله بعد از مورد AUTOCOMPLETE_FRAGMENT_DEMO جای‌گذاری کنید:

    CURRENT_FRAGMENT_DEMO(
        R.string.current_demo_title,
        R.string.current_demo_description,
        CurrentPlaceActivity::class.java
    ),

رشته های مرتبط در فایل src/main/res/values/strings.xml تعریف شده اند.

برنامه را اجرا کنید

  1. برنامه را اجرا کنید. این بار باید سه مورد را در لیست صفحه اصلی مشاهده کنید.
  2. روی ردیف مکان فعلی ضربه بزنید. باید دکمه ای را روی صفحه ببینید.
  3. روی دکمه ضربه بزنید. اگر قبلاً مجوز مکان را به این برنامه نداده‌اید، یک درخواست مجوز باید ظاهر شود.
  4. به برنامه اجازه دسترسی به موقعیت مکانی دستگاه بدهید.
  5. دوباره روی دکمه ضربه بزنید. این بار، همانطور که در شکل 5 نشان داده شده است، فهرستی از حداکثر 20 مکان نزدیک و احتمالات آنها باید ظاهر شود.

ارائه منطبقات احتمالی مکان فعلی برای مکان گزارش شده دستگاه

شکل 5. ارائه منطبقات احتمالی مکان فعلی برای مکان گزارش شده دستگاه.

11. مکان فعلی را روی نقشه نمایش دهید

وابستگی Map را اضافه کنید

در فایل build.gradle در سطح ماژول خود، وابستگی خدمات Google Play را برای Maps SDK برای Android اضافه کنید.

app/build.gradle

dependencies {
    // ...
    implementation 'com.google.android.gms:play-services-maps:18.2.0'
}

مانیفست اندروید را برای حساب نقشه ها به روز کنید

عناصر meta-data زیر را در عنصر application اضافه کنید.

اینها نسخه خدمات Google Play را که برنامه با آن کامپایل شده است جاسازی می کند و کلید API شما را مشخص می کند.

AndroidManifest.xml

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

        <meta-data
            android:name="com.google.android.geo.API_KEY"
            android:value="${MAPS_API_KEY}" />

کلید api را به secrets.properties اضافه کنید

فایل secrets.properties را در دایرکتوری سطح بالای خود باز کنید و سپس کد زیر را اضافه کنید. کلید API خود را جایگزین YOUR_API_KEY کنید.

MAPS_API_KEY=YOUR_API_KEY

فایل local.defaults.properties را در پوشه سطح بالای خود باز کنید، همان پوشه فایل secrets.properties ، و سپس کد زیر را اضافه کنید.

MAPS_API_KEY=DEFAULT_API_KEY

کلید api را بررسی کنید

در onCreate() برنامه کلید API maps را بررسی می کند و قطعه پشتیبانی نقشه ها را مقداردهی اولیه می کند. getMapAsync() برای ثبت نام برای بازگشت به تماس نقشه استفاده می شود.

برای این کار کد زیر را اضافه کنید.

CurrentPlaceActivity.kt

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_current)

        val apiKey = BuildConfig.MAPS_API_KEY

        // Log an error if apiKey is not set.
        if (apiKey.isEmpty() || apiKey == "DEFAULT_API_KEY") {
            Log.e("Places test", "No api key")
            finish()
            return
        }

        // Retrieve a PlacesClient (previously initialized - see DemoApplication)
        placesClient = Places.createClient(this)
        (supportFragmentManager
            .findFragmentById(R.id.map) as SupportMapFragment?)?.getMapAsync(this)

        // ...
    }

طرح بندی نقشه را ایجاد کنید

  1. در پوشه app/src/main/res/layout/ ، فایل layout fragment_map.xml را ایجاد کنید و طرح را با کد زیر پر کنید.

res/layout/fragment_map.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/map"
    android:name="com.google.android.gms.maps.SupportMapFragment"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.google.codelabs.maps.placesdemo.CurrentPlaceActivity" />

این یک SupportMapFragment تعریف می کند تا به عنوان یک محفظه برای نقشه عمل کند و دسترسی به شی GoogleMap را فراهم کند.

  1. در طرح‌بندی activity_current.xml موجود در پوشه app/src/main/res/layout/ ، کد زیر را به پایین طرح‌بندی خطی اضافه کنید.

res/layout/activity_current.xml

    <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="16dp"
        android:paddingBottom="16dp"
        android:text="@string/showing_most_likely_place"
        style="@style/TextAppearance.AppCompat.Title"/>

    <include layout="@layout/fragment_map"/>

TextView اضافه شده به یک منبع رشته جدیدی اشاره می کند که باید ایجاد شود.

  1. در app/src/main/res/values/strings.xml ، منبع رشته زیر را اضافه کنید.

res/values/strings.xml

<string name="showing_most_likely_place">Showing most likely place</string>

از آنجایی که نماهای اضافی برای نقشه اضافه شده است، TextView که لیست مکان ها را نمایش می دهد باید ارتفاع خود را تنظیم کند تا این نماها قابل مشاهده باشند.

  1. ویژگی maxHeight را به TextView با شناسه current_response_content اضافه کنید

res/layout/activity_current.xml

android:maxHeight="200dp"

OnMapReadyCallback پیاده سازی کنید

رابط OnMapReadyCallback را با افزودن آن به اعلان کلاس پیاده‌سازی کنید و متد onMapReady() برای تنظیم نقشه زمانی که شی GoogleMap در دسترس است، لغو کنید:

CurrentPlaceActivity.kt

class CurrentPlaceActivity : AppCompatActivity(), OnMapReadyCallback {

در پایان کلاس کد زیر را اضافه کنید:

CurrentPlaceActivity.kt

    override fun onMapReady(map: GoogleMap) {
        this.map = map
        lastKnownLocation?.let { location ->
            map.moveCamera(
                CameraUpdateFactory.newLatLngZoom(
                    location,
                    DEFAULT_ZOOM
                )
            )
        }
    }

تماس برگشتی به برخی از متغیرهای کلاس نیاز دارد تا به درستی کار کنند. بلافاصله بعد از هدر کلاس، موارد زیر را اضافه کنید:

CurrentPlaceActivity.kt

private var map: GoogleMap? = null
private var lastKnownLocation: LatLng? = null

کد زیر را به شی کلاس companion اضافه کنید:

CurrentPlaceActivity.kt

private const val DEFAULT_ZOOM = 15f

برنامه را اجرا کنید

  1. برنامه را اجرا کنید.
  2. روی ردیف مکان فعلی ضربه بزنید. باید دکمه ای را روی صفحه ببینید.
  3. روی دکمه ضربه بزنید. اگر قبلاً مجوز مکان را به این برنامه نداده‌اید، یک درخواست مجوز باید ظاهر شود.
  4. به برنامه اجازه دسترسی به موقعیت مکانی دستگاه بدهید.
  5. دوباره روی دکمه ضربه بزنید. نقشه نمایش داده خواهد شد.

فعالیت مکان فعلی که نقشه را نشان می دهد

شکل 6. فعالیت مکان فعلی که نقشه را نشان می دهد.

نقشه را با یک مکان به روز کنید

در پایان کلاس کد زیر را اضافه کنید:

CurrentPlaceActivity.kt

private data class LikelyPlace(
    val name: String,
    val address: String,
    val attribution: List<String>,
    val latLng: LatLng
)

private fun PlaceLikelihood.toLikelyPlace(): LikelyPlace? {
    val name = this.place.name
    val address = this.place.address
    val latLng = this.place.latLng
    val attributions = this.place.attributions ?: emptyList()

    return if (name != null && address != null && latLng != null) {
        LikelyPlace(name, address, attributions, latLng)
    } else {
        null
    }
}

اینها برای ذخیره داده های Place و قالب بندی آن استفاده می شود.

در ابتدای کلاس، کد زیر را برای ایجاد متغیری که برای ذخیره داده های Place برگشتی استفاده می شود، اضافه کنید.

CurrentPlaceActivity.kt

private val likelyPlaces = mutableListOf<LikelyPlace>()

در این مرحله، کد تغییر می کند، بنابراین لیستی از مکان ها به کاربر نمایش داده می شود و آنها یکی را برای نمایش روی نقشه انتخاب می کنند. همه داده‌های Places در فهرستی روی صفحه نمایش داده می‌شوند.

در تابع findCurrentPlace ، در بلوک lifecycleScope.launch قبل از این خط کد

CurrentPlaceActivity.kt

responseView.text = response.prettyPrint()

کد زیر را اضافه کنید:

CurrentPlaceActivity.kt

                likelyPlaces.clear()

                likelyPlaces.addAll(
                    response.placeLikelihoods.take(M_MAX_ENTRIES).mapNotNull { placeLikelihood ->
                        placeLikelihood.toLikelyPlace()
                    }
                )

                openPlacesDialog()

این کد به یک ثابت برای نمایش حداکثر تعداد مکان نیاز دارد.

در شیء همراه، کد آن ثابت را اضافه کنید.

CurrentPlaceActivity.kt

private const val M_MAX_ENTRIES = 5

کد زیر را اضافه کنید که گفتگویی را ایجاد می کند که به کاربر اجازه می دهد مکان را انتخاب کند.

CurrentPlaceActivity.kt

    /**
     * Displays a form allowing the user to select a place from a list of likely places.
     */
    private fun openPlacesDialog() {
        // Ask the user to choose the place where they are now.
        val listener =
            DialogInterface.OnClickListener { _, which -> // The "which" argument contains the position of the selected item.
                val likelyPlace = likelyPlaces[which]
                lastKnownLocation = likelyPlace.latLng

                val snippet = buildString {
                    append(likelyPlace.address)
                    if (likelyPlace.attribution.isNotEmpty()) {
                        append("\n")
                        append(likelyPlace.attribution.joinToString(", "))
                    }
                }

                val place = Place.builder().apply {
                    name = likelyPlace.name
                    latLng = likelyPlace.latLng
                }.build()

                map?.clear()

                setPlaceOnMap(place, snippet)
            }

        // Display the dialog.
        AlertDialog.Builder(this)
            .setTitle(R.string.pick_place)
            .setItems(likelyPlaces.map { it.name }.toTypedArray(), listener)
            .setOnDismissListener {
                currentButton.isEnabled = true
            }
            .show()
    }

با پیروی از بهترین شیوه های Android، گفتگو به منبع رشته ای اشاره می کند که باید به فایل منبع strings.xml واقع در پوشه app/src/main/res/values/ اضافه شود.

موارد زیر را به strings.xml اضافه کنید:

res/values/strings.xml

    <string name="pick_place">Choose a place</string>

سپس این توابع تابع setPlaceOnMap را فراخوانی می کنند که دوربین را حرکت می دهد و یک نشانگر را در محل انتخاب شده قرار می دهد.

کد زیر را اضافه کنید:

CurrentPlaceActivity.kt

    private fun setPlaceOnMap(place: Place?, markerSnippet: String?) {
        val latLng = place?.latLng ?: defaultLocation
        map?.moveCamera(
            CameraUpdateFactory.newLatLngZoom(
                latLng,
                DEFAULT_ZOOM
            )
        )
        map?.addMarker(
            MarkerOptions()
                .position(latLng)
                .title(place?.name)
                .snippet(markerSnippet)
        )
    }

همچنین توصیه می شود وضعیت نقشه ها را ذخیره و بازیابی کنید.

برای ذخیره وضعیت آن، تابع onSaveInstanceState را لغو کنید و کد زیر را اضافه کنید:

CurrentPlaceActivity.kt

    /**
     * Saves the state of the map when the activity is paused.
     */
    override fun onSaveInstanceState(outState: Bundle) {
        outState.putParcelable(KEY_LOCATION, lastKnownLocation)
        super.onSaveInstanceState(outState)
    }

برای بازیابی حالت آن، در onCreate پس از تماس با setContentView کد زیر را اضافه کنید:

CurrentPlaceActivity.kt

        if (savedInstanceState != null) {
            lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
        }

ذخیره و بازیابی به یک کلید نیاز دارد، این یک ثابت از شی همراه است.

در بلوک شیء همراه، موارد زیر را اضافه کنید:

CurrentPlaceActivity.kt

        // Key for storing activity state.
        private const val KEY_LOCATION = "location"

برنامه را اجرا کنید

  1. برنامه را اجرا کنید.
  2. روی ردیف مکان فعلی ضربه بزنید. باید دکمه ای را روی صفحه ببینید.
  3. روی دکمه ضربه بزنید. اگر قبلاً مجوز مکان را به این برنامه نداده‌اید، یک درخواست مجوز باید ظاهر شود.
  4. به برنامه اجازه دسترسی به موقعیت مکانی دستگاه بدهید.
  5. دوباره روی دکمه ضربه بزنید.
  6. با ضربه زدن روی آن مکان را انتخاب کنید. نقشه بزرگ‌نمایی می‌شود و با نشانگری که در مکان انتخابی قرار می‌گیرد، در مرکز قرار می‌گیرد.

نقشه با نشانگر در محل انتخاب شده

شکل 7. نقشه با نشانگر در محل انتخاب شده.

12. تبریک می گویم

شما با موفقیت برنامه Android را با Places SDK برای Android ساخته اید.

چیزی که یاد گرفتی

بعدش چی؟

  • برای الهام بیشتر، مخزن android-places-demos GitHub از نمونه‌ها و دموها را کاوش یا جدا کنید.
  • از کدهای Kotlin بیشتر برای ساخت برنامه های اندروید با پلتفرم نقشه های گوگل بیاموزید.
  • با پاسخ دادن به سوال زیر به ما کمک کنید محتوایی را ایجاد کنیم که برای شما مفیدتر باشد:

دوست دارید چه کدهای دیگری را ببینید؟

تجسم داده ها بر روی نقشه ها بیشتر در مورد سفارشی کردن سبک نقشه های من ساختمان برای تعاملات سه بعدی در نقشه ها

آیا کد لبه مورد نظر شما در لیست نیست؟ آن را با یک شماره جدید در اینجا درخواست کنید .