Начало работы с Places SDK для Android (Kotlin),Начало работы с Places SDK для Android (Kotlin)

1. Прежде чем начать

В этой лабораторной работе вы узнаете, как интегрировать Places SDK для Android с вашим приложением и использовать каждую из функций Places SDK.

Демо-приложение Places

Предпосылки

  • Базовые знания Kotlin и разработки под Android

Чему вы научитесь

  • Как установить Places SDK для Android с расширениями Kotlin.
  • Как загрузить информацию о конкретном месте.
  • Как добавить виджет автозаполнения мест в ваше приложение.
  • Как загрузить текущее место на основе текущего местоположения устройства.

Что вам понадобится

Для выполнения этой лабораторной работы вам понадобятся следующие учетные записи, сервисы и инструменты:

2. Настройте

Для выполнения шага включения ниже включите Places API и Maps SDK для Android .

Настройте платформу Google Карт

Если у вас еще нет учетной записи Google Cloud Platform и проекта с включенным выставлением счетов, ознакомьтесь с руководством « Начало работы с Google Maps Platform», чтобы создать учетную запись для выставления счетов и проект.

  1. В Cloud Console щелкните раскрывающееся меню проектов и выберите проект, который вы хотите использовать для этой кодовой лаборатории.

  1. Включите API и SDK платформы Google Карт, необходимые для этой лабораторной работы, в Google Cloud Marketplace . Для этого следуйте инструкциям в этом видео или в этой документации .
  2. Сгенерируйте ключ API на странице «Учётные данные» в Cloud Console. Вы можете следовать инструкциям в этом видео или в этой документации . Для всех запросов к платформе Google Карт требуется ключ API.

3. Быстрый старт

Чтобы начать работу как можно быстрее, скачайте стартовый код, который поможет вам разобраться с этой практической работой. Вы можете сразу перейти к решению, но если хотите выполнить все шаги по его самостоятельной сборке, продолжайте читать.

  1. Клонируйте репозиторий, если у вас установлен git .
git clone https://github.com/googlemaps/codelab-places-101-android-kotlin.git

Или нажмите эту кнопку, чтобы загрузить исходный код.

  1. После загрузки кода откройте проект из каталога /starter в Android Studio. Этот проект содержит базовую структуру файлов, необходимую для выполнения этой лабораторной работы. Всё необходимое для работы находится в каталоге /starter .

Если вы хотите увидеть полный работающий код решения, вы можете просмотреть готовый код в каталоге /solution .

4. Добавьте свой API-ключ в проект.

В этом разделе описывается, как сохранить ключ API, чтобы приложение могло безопасно ссылаться на него. Не следует регистрировать ключ API в системе контроля версий, поэтому мы рекомендуем хранить его в файле secrets.properties , который будет размещён в локальной копии корневого каталога вашего проекта. Подробнее о файле secrets.properties см. в разделе Файлы свойств Gradle .

Чтобы упростить эту задачу, мы рекомендуем вам использовать плагин Secrets Gradle для Android .

Чтобы установить плагин Secrets Gradle для Android в вашем проекте 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 в каталоге верхнего уровня и добавьте следующий код. Замените YOUR_API_KEY вашим ключом API. Сохраните свой ключ в этом файле, поскольку 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 для Android.

В этом разделе вы добавите Places SDK для Android к зависимостям вашего приложения.

  1. Теперь, когда ваш ключ API доступен внутри приложения, добавьте зависимость Places SDK для 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 платформы Google Карт для Android, библиотеки расширения Kotlin (KTX) позволяют использовать возможности языка Kotlin, такие как сопрограммы, свойства/функции расширения и многое другое. Каждому SDK Google Карт соответствует библиотека KTX, как показано ниже:

Диаграмма KTX платформы Google Карт

В этой задаче используйте библиотеку 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.

Инициализируйте 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 .

Создать детальную активность

  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 для использования с этим действием. Вставьте этот код после кода проверки ключа 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 запрошенной информацией.

Добавьте действие Details в манифест

Добавьте элемент <activity> для DetailsActivity как дочерний элемент элемента <application> в файле AndroidManifest.xml , расположенном в app/src/main :

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

Добавить действие «Подробности» в демонстрационное меню

Для отображения доступных демонстраций на главном экране предоставляется пустой модуль Demo . После создания действия Place Details добавьте его в файл 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
    }
}

Этот код связывает активность с представлениями и 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 в манифест

Добавьте элемент <activity> для AutocompleteActivity как дочерний элемент элемента <application> в файле AndroidManifest.xml , расположенном в app/src/main :

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

Добавить действие «Автозаполнение» в демо-меню

Как и прежде, добавьте демо-версию Place Autocomplete на главный экран, добавив её в список модуля Demo . После создания активности Place Autocomplete добавьте её в файл 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()
        }
    }
}

Этот код связывает активность с представлениями, определенными в файле макета. Он также добавляет прослушиватель щелчков к кнопке для вызова функции 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, он может предоставить только разрешение на определение точного местоположения. Если пользователь использует устройство с Android 12 или выше, ему будет предоставлена возможность указать приблизительное местоположение вместо точного, как показано на рисунке 4.

Запрос разрешения пользователя на устройстве под управлением Android 12 или выше

Рисунок 4. Запрос разрешения пользователя на устройстве под управлением Android 12 или выше предоставляет возможность предоставления точного или приблизительного местоположения.

После того, как пользователь ответит на диалоговое окно системных разрешений, система вызовет реализацию метода onRequestPermissionsResult вашего приложения. Система передаёт ответ пользователя на диалоговое окно разрешений, а также определённый вами код запроса. Переопределите onRequestPermissionResult для обработки кода запроса разрешений местоположения, связанных с этим действием Current Place, вставив следующий код ниже 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 запрошенными данными.

Добавьте действие «Текущее место» в манифест

Добавьте элемент <activity> для CurrentPlaceActivity как дочерний элемент элемента <application> в файле AndroidManifest.xml , расположенном в app/src/main :

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

Добавить действие «Текущее место» в демо-меню

Как и раньше, добавьте демо-версию 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. Нажмите кнопку ещё раз. На этот раз должен появиться список из 20 близлежащих мест и их вероятности, как показано на рисунке 5.

Представление вероятных совпадений текущего места для указанного местоположения устройства

Рисунок 5. Представление вероятных совпадений текущего места для указанного местоположения устройства.

11. Отображение текущего места на карте

Добавьте зависимость карты

В файле build.gradle на уровне модуля добавьте зависимость сервисов Google Play для Maps SDK для Android.

app/build.gradle

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

Обновите манифест Android для учета карт

Добавьте следующие элементы 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 в каталоге верхнего уровня и добавьте следующий код. Замените YOUR_API_KEY на свой ключ API.

MAPS_API_KEY=YOUR_API_KEY

Откройте файл local.defaults.properties в каталоге верхнего уровня, в той же папке, что и файл secrets.properties , а затем добавьте следующий код.

MAPS_API_KEY=DEFAULT_API_KEY

Проверьте наличие ключа API

В onCreate() приложение проверит ключ API карт и инициализирует фрагмент поддержки карт. 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/ создайте файл макета 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

Добавьте следующий код к объекту-компаньону класса:

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.

CurrentPlaceActivity.kt

private val likelyPlaces = mutableListOf<LikelyPlace>()

На этом этапе код будет изменён таким образом, что пользователю будет представлен список мест, из которого он сможет выбрать одно для отображения на карте. Все данные о местах отображаются в виде списка на экране.

В функции 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.

Что вы узнали

Что дальше?

Какие еще практические занятия вы хотели бы увидеть?

Визуализация данных на картах Подробнее о настройке стиля моих карт Создание 3D-взаимодействий на картах

Нужная вам кодлаб-программа отсутствует в списке? Запросите её в новом выпуске здесь .

,

1. Прежде чем начать

В этой лабораторной работе вы узнаете, как интегрировать Places SDK для Android с вашим приложением и использовать каждую из функций Places SDK.

Демо-приложение Places

Предпосылки

  • Базовые знания Kotlin и разработки под Android

Чему вы научитесь

  • Как установить Places SDK для Android с расширениями Kotlin.
  • Как загрузить информацию о конкретном месте.
  • Как добавить виджет автозаполнения мест в ваше приложение.
  • Как загрузить текущее место на основе текущего местоположения устройства.

Что вам понадобится

Для выполнения этой лабораторной работы вам понадобятся следующие учетные записи, сервисы и инструменты:

2. Настройте

Для выполнения шага включения ниже включите Places API и Maps SDK для Android .

Настройте платформу Google Карт

Если у вас еще нет учетной записи Google Cloud Platform и проекта с включенным выставлением счетов, ознакомьтесь с руководством « Начало работы с Google Maps Platform», чтобы создать учетную запись для выставления счетов и проект.

  1. В Cloud Console щелкните раскрывающееся меню проектов и выберите проект, который вы хотите использовать для этой кодовой лаборатории.

  1. Включите API и SDK платформы Google Карт, необходимые для этой лабораторной работы, в Google Cloud Marketplace . Для этого следуйте инструкциям в этом видео или в этой документации .
  2. Сгенерируйте ключ API на странице «Учётные данные» в Cloud Console. Вы можете следовать инструкциям в этом видео или в этой документации . Для всех запросов к платформе Google Карт требуется ключ API.

3. Быстрый старт

Чтобы начать работу как можно быстрее, скачайте стартовый код, который поможет вам разобраться с этой практической работой. Вы можете сразу перейти к решению, но если хотите выполнить все шаги по его самостоятельной сборке, продолжайте читать.

  1. Клонируйте репозиторий, если у вас установлен git .
git clone https://github.com/googlemaps/codelab-places-101-android-kotlin.git

Или нажмите эту кнопку, чтобы загрузить исходный код.

  1. После загрузки кода откройте проект из каталога /starter в Android Studio. Этот проект содержит базовую структуру файлов, необходимую для выполнения этой лабораторной работы. Всё необходимое для работы находится в каталоге /starter .

Если вы хотите увидеть полный работающий код решения, вы можете просмотреть готовый код в каталоге /solution .

4. Добавьте свой API-ключ в проект.

В этом разделе описывается, как сохранить ключ API, чтобы приложение могло безопасно ссылаться на него. Не следует регистрировать ключ API в системе контроля версий, поэтому мы рекомендуем хранить его в файле secrets.properties , который будет размещён в локальной копии корневого каталога вашего проекта. Подробнее о файле secrets.properties см. в разделе Файлы свойств Gradle .

Чтобы упростить эту задачу, мы рекомендуем вам использовать плагин Secrets Gradle для Android .

Чтобы установить плагин Secrets Gradle для Android в вашем проекте 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 в каталоге верхнего уровня и добавьте следующий код. Замените YOUR_API_KEY вашим ключом API. Сохраните свой ключ в этом файле, поскольку 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 для Android.

В этом разделе вы добавите Places SDK для Android к зависимостям вашего приложения.

  1. Теперь, когда ваш ключ API доступен внутри приложения, добавьте зависимость Places SDK для 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 платформы Google Карт для Android, библиотеки расширения Kotlin (KTX) позволяют использовать возможности языка Kotlin, такие как сопрограммы, свойства/функции расширения и многое другое. Каждому SDK Google Карт соответствует библиотека KTX, как показано ниже:

Диаграмма KTX платформы Google Карт

В этой задаче используйте библиотеку 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.

Инициализируйте 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 .

Создать детали деятельности

  1. Создайте файл DetailsActivity.kt в src/main/java/com/google/codelabs/maps/placesdemo/ Polder и свяжите его с только что созданным вами макетом. Вставьте этот код в файл:
@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 для использования с этой деятельностью. Вставьте этот код после кода, чтобы проверить ключ API в функции onCreate .
        // Retrieve a PlacesClient (previously initialized - see DemoApplication)
        placesClient = Places.createClient(this)
  1. После того, как клиент будет настроен, прикрепите прослушитель нажмите кнопку. Вставьте этот код после создания клиентов в функции 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 и заметьте, что он создает лист обзор, который заполняется итерацией через содержимое Demo -модуля. Если пользователь нажимает на элемент в списке, The Click Suldier открывает связанную деятельность.

Запустите приложение

  1. Запустить приложение. На этот раз вы должны увидеть один пункт в списке, представляющий демо -подробную информацию о месте.
  2. Нажмите на место с подробностями. Текст. Вы должны увидеть представление, которое вы создали с помощью поля ввода и кнопки.
  3. Нажмите кнопку «Получить детали». Если вы использовали идентификатор места по умолчанию, вы должны увидеть отображаемые имя места, адрес и координаты карты, как показано на рисунке 1.

Поместите детали деятельности с ответом

Рисунок 1. Разместите детали деятельности с отображением ответа.

9. Добавьте место автозаполнения

Создать экран автозаполнения

В app/src/main/res/layout/ LinearLayout Заполните линейную макет activity_autocomplete.xml добавив этот код между кронштейнами <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
    }
}

Этот код связывает деятельность с представлениями и AutocompleteSupportFramgent который вы определили в файле макета.

  1. Затем определите, что происходит, когда пользователь выбирает одно из предсказаний, представленных AutoCoplete Place. Добавьте этот код в конце функции 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; Нет необходимости делать отдельный запрос с подробностями места при использовании виджета автозаполнения места.

Добавьте активность автозаполнения в манифест

Добавьте элемент <activity> для AutocompleteActivity в качестве ребенка элемента <application> в файле AndroidManifest.xml , расположенном в app/src/main :

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

Добавьте активность автозаполнения в демо -меню

Как и прежде, добавьте Dep AutoComplete Demo на домашний экран, добавив его в список в Demo модуле. Теперь, когда вы создали активность автозаполнения места, добавьте его в файл Demo.kt в src/main/java/com/google/codelabs/maps/placesdemo/ Foper. Вставьте этот код сразу после элемента 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. Получите текущее место устройства

Создать текущий экран места

В app/src/main/res/layout/ LinearLayout Заполните линейную макет activity_current.xml добавив следующий код между кронштейнами <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()
        }
    }
}

Этот код связывает деятельность с представлениями, которые вы определили в файле макета. Он также добавляет кнопку «Прослушитель», чтобы вызвать функцию 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 Function Calls requestPermissions , приложение представит диалоговое окно запроса на разрешение пользователю. Если пользователь запускает устройство, использующую ОС, ниже, чем Android 12, пользователь может предоставить только точное (отличное) разрешение на местоположение. Если пользователь использует устройство под управлением Android 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 запрашиваемыми деталями.

Добавьте текущее место в манифесте

Добавьте элемент <activity> для CurrentPlaceActivity в качестве ребенка элемента <application> в файле AndroidManifest.xml , расположенном в app/src/main :

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

Добавьте текущее место в меню демонстрации

Как и прежде, добавьте текущую демонстрацию места на домашний экран, добавив его в список в Demo модуле. Теперь, когда вы создали текущую деятельность по месту, добавьте его в файл 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. Снова нажмите кнопку. На этот раз должен появиться список до 20 мест, и их вероятность, как показано на рисунке 5.

Представление вероятных совпадений текущих мест для указанного устройства местоположения

Рисунок 5. Представление вероятных совпадений текущего места для указанного устройства.

11. Показать текущее место на карте

Добавить зависимость карты

В вашем файле build.gradle на уровне модуля добавьте зависимость служб Google Play для SDK для Android.

app/build.gradle

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

Обновите манифест Android, чтобы учесть карты

Добавьте следующие элементы meta-data в элемент application .

Они встраивают версию Google Play Services, с которой приложение было составлено, и указывает ваш ключ 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 в вашем каталоге верхнего уровня, а затем добавьте следующий код. Замените YOUR_API_KEY на ключ API.

MAPS_API_KEY=YOUR_API_KEY

Откройте файл local.defaults.properties в вашем каталоге верхнего уровня, в той же папке, что и файл secrets.properties , а затем добавьте следующий код.

MAPS_API_KEY=DEFAULT_API_KEY

Проверьте на ключ API

В onCreate() приложение проверит на наличие ключа API MAPS и инициализировать фрагмент поддержки 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/ polder создайте фрагмент файла 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 с id 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

Добавьте следующий код в объект Class 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
    }
}

Они используются для хранения данных и форматирования их.

В начале класса добавьте следующий код для создания переменной, используемой для хранения данных возвращенного места.

CurrentPlaceActivity.kt

private val likelyPlaces = mutableListOf<LikelyPlace>()

На этом этапе код будет изменен, поэтому пользователю будет представлен список мест, и он выберет один, чтобы показать на карте. Все данные места показаны в списке на экране.

В функции 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/ folder.

Добавьте следующее в 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 с местами SDK для Android.

Что вы узнали

Что дальше?

  • Исследуйте или разветвляйте репозиторий GitHub android-places-demos образцов и демонстраций для большего вдохновения.
  • Учитесь у Cotlin CodeLabs для создания приложений Android с платформой Google Maps.
  • Помогите нам создать контент, который вы найдете наиболее полезным, ответив на следующий вопрос:

Какие еще коделаб вы бы хотели увидеть?

Визуализация данных на картах Подробнее о настройке стиля моих карт Здание для 3D -взаимодействий в картах

Коделаб, который вы хотите, не перечислили? Запросите его с новой проблемой здесь .