Erste Schritte mit dem Places SDK for Android (Kotlin)

1. Vorbereitung

In diesem Codelab erfahren Sie, wie Sie das Places SDK for Android in Ihre App einbinden und die einzelnen Funktionen des SDK verwenden.

Demo-App für Places

Vorbereitung

  • Grundkenntnisse in Kotlin und Android-Entwicklung

Lerninhalte

  • So installieren Sie das Places SDK for Android mit Kotlin-Erweiterungen.
  • So laden Sie Ortsdetails für einen bestimmten Ort.
  • So fügen Sie ein Place Autocomplete-Widget in Ihre App ein.
  • So laden Sie den aktuellen Ort basierend auf dem derzeit gemeldeten Standort des Geräts.

Voraussetzungen

Für dieses Codelab benötigen Sie die folgenden Konten, Dienste und Tools:

2. Einrichten

Aktivieren Sie im folgenden Schritt die Places API und das Maps SDK for Android.

Google Maps Platform einrichten

Wenn Sie noch kein Google Cloud-Konto und kein Projekt mit aktivierter Abrechnung haben, lesen Sie bitte den Leitfaden Erste Schritte mit Google Maps Platform, um ein Rechnungskonto und ein Projekt zu erstellen.

  1. Klicken Sie in der Cloud Console auf das Drop-down-Menü für das Projekt und wählen Sie das Projekt aus, das Sie für dieses Codelab verwenden möchten.

  1. Aktivieren Sie die für dieses Codelab erforderlichen APIs und SDKs der Google Maps Platform im Google Cloud Marketplace. Folgen Sie dazu der Anleitung in diesem Video oder dieser Dokumentation.
  2. Generieren Sie einen API-Schlüssel in der Cloud Console auf der Seite Anmeldedaten. Folgen Sie dazu dieser Anleitung oder dieser Dokumentation. Für alle Anfragen an die Google Maps Platform ist ein API-Schlüssel erforderlich.

3. Schnelleinstieg

Laden Sie den Startcode herunter, damit Sie dieses Codelab nachvollziehen können. Sie können natürlich direkt zur Lösung springen. Wenn Sie die Lösung jedoch selbst erstellen möchten, lesen Sie weiter.

  1. Klonen Sie das Repository, wenn Sie git installiert haben.
git clone https://github.com/googlemaps/codelab-places-101-android-kotlin.git

Alternativ können Sie auf diese Schaltfläche klicken, um den Quellcode herunterzuladen.

  1. Öffnen Sie nach dem Herunterladen des Codes das Projekt im Verzeichnis /starter in Android Studio. Dieses Projekt enthält die grundlegende Dateistruktur, die Sie für das Codelab benötigen. Alle benötigten Dateien befinden sich im Verzeichnis /starter.

Wenn Sie den vollständigen Lösungscode sehen möchten, können Sie ihn im Verzeichnis /solution aufrufen.

4. API-Schlüssel zum Projekt hinzufügen

In diesem Abschnitt wird beschrieben, wie Sie Ihren API-Schlüssel speichern, damit er von Ihrer App sicherer referenziert werden kann. Er sollte nicht in Ihrem Versionsverwaltungssystem eingecheckt werden. Stattdessen empfehlen wir Ihnen, ihn in der Datei secrets.properties zu speichern, die in Ihrer lokalen Kopie des Stammverzeichnisses Ihres Projekts abgelegt wird. Weitere Informationen zur Datei secrets.properties finden Sie unter Gradle-Attributdateien.

Sie können das Secrets Gradle-Plug-in für Android verwenden, um diese Aufgabe zu optimieren.

So installieren Sie das Secrets Gradle-Plug-in für Android in Ihrem Google Maps-Projekt:

  1. Öffnen Sie in Android Studio die Datei build.gradle.kts oder build.gradle auf oberster Ebene und fügen Sie den folgenden Code in das dependencies-Element unter buildscript ein.

Wenn Sie build.gradle.kts verwenden, fügen Sie Folgendes hinzu:

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

Wenn Sie build.gradle verwenden, fügen Sie Folgendes hinzu:

buildscript {
    dependencies {
        classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
    }
}
  1. Öffnen Sie die Datei build.gradle.kts oder build.gradle auf Modulebene und fügen Sie den folgenden Code in das plugins-Element ein.

Wenn Sie build.gradle.kts verwenden, fügen Sie Folgendes hinzu:

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

Wenn Sie build.gradle verwenden, fügen Sie Folgendes hinzu:

plugins {
    // ...
    id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
  1. In der Datei build.gradle.kts oder build.gradle auf Modulebene müssen targetSdk und compileSdk auf 34 gesetzt sein.
  2. Speichern Sie die Datei und synchronisieren Sie Ihr Projekt mit Gradle.
  3. Öffnen Sie die Datei secrets.properties im Verzeichnis der obersten Ebene und fügen Sie den folgenden Code ein. Ersetzen Sie dabei YOUR_API_KEY durch Ihren eigenen API-Schlüssel. Speichern Sie den Schlüssel in dieser Datei, da secrets.properties nicht in ein Versionsverwaltungssystem eingecheckt werden kann.
PLACES_API_KEY=YOUR_API_KEY
  1. Speichern Sie die Datei.
  2. Erstellen Sie die Datei local.defaults.properties im Verzeichnis der obersten Ebene, im selben Ordner wie die Datei secrets.properties, und fügen Sie folgenden Code ein.
PLACES_API_KEY=DEFAULT_API_KEY

Diese Datei ist ein Ersatzspeicherort für den API-Schlüssel, damit Builds nicht fehlschlagen, falls die Datei secrets.properties nicht gefunden wird. Das kann passieren, wenn Sie die App aus einem Versionsverwaltungssystem klonen, in dem secrets.properties nicht vorhanden ist, und noch keine lokale Datei secrets.properties erstellt haben, um Ihren API-Schlüssel bereitzustellen.

  1. Speichern Sie die Datei.
  2. Öffnen Sie in Android Studio die Datei build.gradle.kts oder build.gradle auf Modulebene und bearbeiten Sie das Attribut secrets. Wenn die Property secrets nicht vorhanden ist, fügen Sie sie hinzu.

Bearbeiten Sie die Attribute des Plug-ins, um propertiesFileName auf secrets.properties, defaultPropertiesFileName auf local.defaults.properties und alle anderen Attribute festzulegen.

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 for Android installieren

In diesem Abschnitt fügen Sie die Abhängigkeiten des Places SDK for Android in Ihre App ein.

  1. Nachdem auf Ihren API-Schlüssel in der App zugegriffen werden kann, fügen Sie die Abhängigkeit für das Places SDK for Android in die Datei build.gradle Ihrer App ein.

Fügen Sie der Datei build.gradle auf App-Ebene die Abhängigkeit für das Places SDK for Android hinzu:

build.gradle-Datei auf App-Ebene

dependencies {
   // Dependency to include Places SDK for Android
   implementation 'com.google.android.libraries.places:places:3.4.0'
}
  1. Starten Sie die App.

Sie sollten jetzt eine App mit einem leeren Bildschirm sehen. Füge diesem Bildschirm drei Demos hinzu.

6. Places Android KTX installieren

Für Kotlin-Apps, die ein oder mehrere Google Maps Platform Android SDKs verwenden, können Sie mit KTX-Bibliotheken (Kotlin Extensions) Kotlin-Sprachfunktionen wie Koroutinen, Erweiterungseigenschaften/-funktionen und mehr nutzen. Für jedes Google Maps SDK gibt es eine entsprechende KTX-Bibliothek, wie unten dargestellt:

Google Maps Platform – KTX-Diagramm

In dieser Aufgabe verwenden Sie die Places Android KTX-Bibliothek, um Kotlin-spezifische Sprachfunktionen in Ihrer App zu nutzen.

Places Android KTX-Abhängigkeit hinzufügen

Wenn Sie Kotlin-spezifische Funktionen nutzen möchten, fügen Sie die entsprechende KTX-Bibliothek für dieses SDK in Ihre build.gradle-Datei auf App-Ebene ein.

build.gradle

dependencies {
    // ...

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

7. Places-Client initialisieren

Places SDK für den Anwendungsbereich initialisieren

Initialisieren Sie das Places SDK for Android in der Datei DemoApplication.kt des Ordners app/src/main/java/com/google/codelabs/maps/placesdemo. Fügen Sie die folgenden Zeilen am Ende der Funktion onCreate ein:

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

Wenn Sie Ihre App erstellen, wird der API-Schlüssel in Ihrer secrets.properties-Datei über das Secrets Gradle-Plug-in für Android als BuildConfig.PLACES_API_KEY verfügbar gemacht.

Anwendungsdatei zum Manifest hinzufügen

Da Sie Application mit DemoApplication erweitert haben, müssen Sie das Manifest aktualisieren. Fügen Sie dem Element application in der Datei AndroidManifest.xml, die sich in app/src/main befindet, das Attribut android:name hinzu:

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

Mit diesem Code wird im Anwendungsmanifest auf die Klasse DemoApplication im Ordner src/main/java/com/google/codelabs/maps/placesdemo/ verwiesen.

8. Ortsdetails abrufen

Detailbildschirm erstellen

Ein activity_details.xml-Layout mit einem leeren LinearLayout ist im Ordner app/src/main/res/layout/ verfügbar. Fügen Sie den folgenden Code zwischen den <LinearLayout>-Klammern ein, um das lineare Layout zu füllen.

    <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" />

Mit diesem Code wird ein Texteingabefeld hinzugefügt, in das der Nutzer eine beliebige Orts-ID eingeben oder den bereitgestellten Standardwert verwenden kann. Außerdem wird eine Schaltfläche zum Initiieren der „Place Details“-Anfrage und eine TextView zum Anzeigen der Informationen aus der Antwort hinzugefügt. Die zugehörigen Strings sind für Sie in der Datei src/main/res/values/strings.xml definiert.

Detailaktivität erstellen

  1. Erstellen Sie im Ordner src/main/java/com/google/codelabs/maps/placesdemo/ eine Datei DetailsActivity.kt und verknüpfen Sie sie mit dem gerade erstellten Layout. Fügen Sie diesen Code in die Datei ein:
@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. Erstellen Sie einen Places-Client für die Verwendung mit dieser Aktivität. Fügen Sie diesen Code nach dem Code zum Prüfen des API-Schlüssels in der Funktion onCreate ein.
        // Retrieve a PlacesClient (previously initialized - see DemoApplication)
        placesClient = Places.createClient(this)
  1. Nachdem der Places Client eingerichtet wurde, hängen Sie einen Klick-Listener an die Schaltfläche an. Fügen Sie diesen Code nach der Erstellung des Places-Clients in die Funktion onCreate ein.
        // 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
            }
        }

Mit diesem Code wird die Orts-ID abgerufen, die in das Eingabefeld eingegeben wurde. Außerdem wird definiert, welche Felder für den Ort angefordert werden sollen. Dann wird ein FetchPlaceRequest erstellt, die Aufgabe wird gestartet und es wird auf Erfolg oder Fehler gewartet. Wenn die Anfrage erfolgreich ist, werden die angeforderten Details in der TextView angezeigt.

Aktivität „Details“ dem Manifest hinzufügen

Fügen Sie in der Datei AndroidManifest.xml unter app/src/main ein <activity>-Element für die DetailsActivity als untergeordnetes Element des <application>-Elements hinzu:

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

Aktivität „Details“ dem Demomenü hinzufügen

Ein leeres Demo-Modul wird bereitgestellt, um die verfügbaren Demos auf dem Startbildschirm aufzulisten. Nachdem Sie eine Aktivität für Ortsdetails erstellt haben, fügen Sie sie mit diesem Code der Datei Demo.kt im Ordner src/main/java/com/google/codelabs/maps/placesdemo/ hinzu:

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

Die zugehörigen Strings sind in der Datei src/main/res/values/strings.xml definiert.

Sehen Sie sich MainActivity.kt an. Dort wird eine ListView erstellt, die durch Iterieren durch den Inhalt des Moduls Demo gefüllt wird. Wenn der Nutzer auf ein Element in der Liste tippt, öffnet der Klick-Listener die zugehörige Aktivität.

Anwendung ausführen

  1. Führen Sie die App aus. Dieses Mal sollte ein Element in der Liste angezeigt werden, das die Demo für Ortsdetails enthält.
  2. Tippen Sie auf den Text „Ortsdetails“. Sie sollten die von Ihnen erstellte Ansicht mit einem Eingabefeld und einer Schaltfläche sehen.
  3. Tippen Sie auf die Schaltfläche „DETAILS ANSEHEN“. Wenn Sie die Standard-Orts-ID verwendet haben, sollten der Ortsname, die Adresse und die Kartenkoordinaten angezeigt werden, wie in Abbildung 1 dargestellt.

„Place Details“-Aktivität mit Antwort

Abbildung 1. Aktivität „Ortsdetails“ mit angezeigter Antwort.

9. „Place Autocomplete“ verwenden

Bildschirm für die automatische Vervollständigung erstellen

Ein activity_autocomplete.xml-Layout mit einem leeren LinearLayout ist im Ordner app/src/main/res/layout/ verfügbar. Fügen Sie diesen Code zwischen den <LinearLayout>-Klammern ein, um das lineare Layout zu erstellen.

    <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" />

Dieser Code fügt ein AutocompleteSupportFragment-Widget und eine TextView hinzu, um die Informationen aus der Antwort anzuzeigen. Die zugehörigen Strings sind in der Datei src/main/res/values/strings.xml definiert.

Aktivität zur automatischen Vervollständigung erstellen

  1. Erstellen Sie eine AutocompleteActivity.kt-Datei im Ordner src/main/java/com/google/codelabs/maps/placesdemo/ und definieren Sie sie mit diesem Code:
@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
    }
}

Mit diesem Code wird die Aktivität den Ansichten und AutocompleteSupportFramgent zugeordnet, die Sie in der Layoutdatei definiert haben.

  1. Legen Sie als Nächstes fest, was passiert, wenn der Nutzer einen der von Place Autocomplete präsentierten Vorschläge auswählt. Fügen Sie diesen Code am Ende der Funktion onCreate ein:
        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()
                }
            }
        }

Dieser Code definiert, welche Felder für den Ort angefordert werden sollen, und wartet auf ein Ereignis zur Ortsauswahl sowie auf Erfolg oder Fehler. Wenn die Anfrage erfolgreich ist, werden die Ortsdetails in der TextView angezeigt. Beachten Sie, dass Place Autocomplete ein Place-Objekt zurückgibt. Wenn Sie das Place Autocomplete-Widget verwenden, ist keine separate „Place Details“-Anfrage erforderlich.

Autocomplete-Aktivität dem Manifest hinzufügen

Fügen Sie in der Datei AndroidManifest.xml unter app/src/main ein <activity>-Element für die AutocompleteActivity als untergeordnetes Element des <application>-Elements hinzu:

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

Autocomplete-Aktivität dem Demomenü hinzufügen

Fügen Sie die Place Autocomplete-Demo wie zuvor dem Startbildschirm hinzu, indem Sie sie an die Liste im Demo-Modul anhängen. Nachdem Sie eine Place Autocomplete-Aktivität erstellt haben, fügen Sie sie der Datei Demo.kt im Ordner src/main/java/com/google/codelabs/maps/placesdemo/ hinzu. Fügen Sie diesen Code direkt nach dem Element DETAILS_FRAGMENT_DEMO ein:

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

Die zugehörigen Strings sind in der Datei src/main/res/values/strings.xml definiert.

Anwendung ausführen

  1. Führen Sie die App aus. Dieses Mal sollten Sie zwei Elemente in der Liste auf dem Startbildschirm sehen.
  2. Tippen Sie auf die Zeile „Place Autocomplete“. Es sollte ein Place Autocomplete-Eingabefeld wie in Abbildung 2 angezeigt werden.
  3. Geben Sie den Namen eines Orts ein. Das kann ein Name, eine Adresse oder eine geografische Region sein. Vorhersagen sollten während der Eingabe angezeigt werden.
  4. Wählen Sie einen der Vorschläge aus. Die Vorhersagen sollten verschwinden und im TextView sollten jetzt die Details zum ausgewählten Ort angezeigt werden, wie in Abbildung 3 dargestellt.

Automatische Vervollständigung nach dem Tippen des Nutzers auf das Eingabefeld

Abbildung 2: Automatische Vervollständigung von Aktivitäten, nachdem der Nutzer auf das Eingabefeld getippt hat.

Vervollständigen der Aktivität, nachdem der Nutzer „Niagarafälle“ eingegeben und ausgewählt hat

Abbildung 3: Aktivität zur automatischen Vervollständigung, in der Ortsdetails angezeigt werden, nachdem der Nutzer „Niagara Falls“ eingegeben und ausgewählt hat.

10. Aktuellen Ort des Geräts abrufen

Bildschirm „Aktueller Ort“ erstellen

Im Ordner app/src/main/res/layout/ wurde ein activity_current.xml-Layout mit einem leeren LinearLayout bereitgestellt. Fügen Sie den folgenden Code zwischen den <LinearLayout>-Klammern ein, um das lineare Layout zu füllen.

    <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" />

„Aktueller Ort“-Aktivität erstellen

  1. Erstellen Sie eine CurrentPlaceActivity.kt-Datei im Ordner src/main/java/com/google/codelabs/maps/placesdemo/ und definieren Sie sie mit diesem Code:
@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()
        }
    }
}

Mit diesem Code wird die Aktivität den Ansichten zugeordnet, die Sie in der Layoutdatei definiert haben. Außerdem wird ein Click-Listener für die Schaltfläche hinzugefügt, um die Funktion checkPermissionThenFindCurrentPlace aufzurufen, wenn auf die Schaltfläche geklickt wird.

  1. Definieren Sie checkPermissionThenFindCurrentPlace(), um die Berechtigung für den genauen Standort zu prüfen und die Berechtigung anzufordern, falls sie noch nicht erteilt wurde. Fügen Sie diesen Code nach der onCreate-Funktion ein.
    /**
     * 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. Wenn im else-Zweig der Funktion checkPermissionThenFindCurrentPlace die Funktion requestPermissions aufgerufen wird, wird dem Nutzer in der App ein Dialogfeld mit einer Berechtigungsanfrage angezeigt. Wenn der Nutzer ein Gerät mit einer niedrigeren Version als Android 12 verwendet, kann er nur die Berechtigung für den genauen (feinen) Standort gewähren. Wenn der Nutzer ein Gerät mit Android 12 oder höher verwendet, hat er die Möglichkeit, anstelle des genauen Standorts den ungefähren Standort anzugeben (siehe Abbildung 4).

Nutzerberechtigung auf einem Gerät mit Android 12 oder höher anfordern

Abbildung 4: Wenn Sie auf einem Gerät mit Android 12 oder höher eine Nutzerberechtigung anfordern, haben Sie die Möglichkeit, den genauen oder ungefähren Standort zu gewähren.

Nachdem der Nutzer auf das Dialogfeld für Systemberechtigungen geantwortet hat, ruft das System die Implementierung von onRequestPermissionsResult in Ihrer App auf. Das System übergibt die Nutzerantwort auf das Berechtigungsdialogfeld sowie den von Ihnen definierten Anfragecode. Überschreiben Sie onRequestPermissionResult, um den Anfragecode für die Standortberechtigungen im Zusammenhang mit dieser Aktivität „Aktueller Ort“ zu verarbeiten. Fügen Sie dazu den folgenden Code unter checkPermissionThenFindCurrentPlace ein.

    @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. Nachdem die Berechtigung erteilt wurde, wird die Funktion findCurrentPlace ausgeführt. Definieren Sie die Funktion mit diesem Code nach der Funktion 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()
        }
    }

Dieser Code definiert, welche Felder für die wahrscheinlichen Orte angefordert werden sollen, erstellt ein FindCurrentPlaceRequest, initiiert die Aufgabe und füllt die TextView mit den angeforderten Details.

Aktivität „Aktueller Ort“ dem Manifest hinzufügen

Fügen Sie in der Datei AndroidManifest.xml unter app/src/main ein <activity>-Element für die CurrentPlaceActivity als untergeordnetes Element des <application>-Elements hinzu:

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

Aktivität „Aktueller Ort“ dem Demomenü hinzufügen

Fügen Sie die Demo „Aktueller Ort“ wie zuvor dem Startbildschirm hinzu, indem Sie sie der Liste im Modul Demo anhängen. Nachdem Sie eine „Aktueller Ort“-Aktivität erstellt haben, fügen Sie sie der Datei Demo.kt im Ordner src/main/java/com/google/codelabs/maps/placesdemo/ hinzu. Fügen Sie diesen Code direkt nach dem Element AUTOCOMPLETE_FRAGMENT_DEMO ein:

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

Die zugehörigen Strings sind in der Datei src/main/res/values/strings.xml definiert.

Anwendung ausführen

  1. Führen Sie die App aus. Dieses Mal sollten drei Elemente in der Liste auf dem Startbildschirm angezeigt werden.
  2. Tippen Sie auf die Zeile „Aktueller Ort“. Auf dem Bildschirm sollte eine Schaltfläche angezeigt werden.
  3. Tippe auf die Schaltfläche. Wenn Sie dieser App noch keine Berechtigung zur Standortermittlung erteilt haben, sollte eine Berechtigungsanfrage angezeigt werden.
  4. Erteilen Sie der App die Berechtigung, auf den Standort des Geräts zuzugreifen.
  5. Tippen Sie noch einmal auf die Schaltfläche. Nun sollte eine Liste mit bis zu 20 Orten in der Nähe und den entsprechenden Wahrscheinlichkeiten angezeigt werden (siehe Abbildung 5).

Wahrscheinliche Übereinstimmungen für den aktuellen Ort für den gemeldeten Standort des Geräts anzeigen

Abbildung 5: Es werden wahrscheinliche Übereinstimmungen für den aktuellen Ort des Geräts angezeigt.

11. Aktuellen Ort auf einer Karte anzeigen

Maps-Abhängigkeit hinzufügen

Fügen Sie in der Datei build.gradle auf Modulebene die Google Play-Dienste-Abhängigkeit für das Maps SDK for Android ein.

app/build.gradle

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

Android-Manifest für Karten aktualisieren

Fügen Sie die folgenden meta-data-Elemente in das application-Element ein.

Damit wird die Version der Google Play-Dienste eingebettet, mit der die App kompiliert wurde, und Ihr API-Schlüssel wird angegeben.

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-Schlüssel zu secrets.properties hinzufügen

Öffnen Sie die Datei secrets.properties im Verzeichnis der obersten Ebene und fügen Sie den folgenden Code ein. Ersetzen Sie dabei YOUR_API_KEY durch Ihren eigenen API-Schlüssel.

MAPS_API_KEY=YOUR_API_KEY

Öffnen Sie die Datei local.defaults.properties im Verzeichnis der obersten Ebene, im selben Ordner wie die Datei secrets.properties, und fügen Sie folgenden Code ein.

MAPS_API_KEY=DEFAULT_API_KEY

Nach dem API-Schlüssel suchen

In onCreate() wird der Maps API-Schlüssel geprüft und das Fragment für die Kartenunterstützung initialisiert. getMapAsync() wird verwendet, um sich für den Karten-Callback zu registrieren.

Fügen Sie dazu den folgenden Code ein.

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)

        // ...
    }

Kartenlayout erstellen

  1. Erstellen Sie im Ordner app/src/main/res/layout/ die Layoutdatei fragment_map.xml und fügen Sie den folgenden Code in das Layout ein.

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" />

Damit wird ein SupportMapFragment definiert, das als Container für die Karte fungiert und Zugriff auf das GoogleMap-Objekt bietet.

  1. Fügen Sie im activity_current.xml-Layout, das im Ordner app/src/main/res/layout/ verfügbar ist, den folgenden Code am Ende des linearen Layouts hinzu.

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"/>

Die hinzugefügte TextView verweist auf eine neue String-Ressource, die erstellt werden muss.

  1. Fügen Sie in app/src/main/res/values/strings.xml die folgende String-Ressource hinzu.

res/values/strings.xml

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

Da der Karte zusätzliche Ansichten hinzugefügt wurden, muss die Höhe des TextView, in dem die Liste der Orte angezeigt wird, so festgelegt werden, dass diese Ansichten sichtbar bleiben.

  1. Fügen Sie das Attribut maxHeight dem TextView mit der ID current_response_content hinzu.

res/layout/activity_current.xml

android:maxHeight="200dp"

OnMapReadyCallback implementieren

Implementieren Sie die OnMapReadyCallback-Schnittstelle, indem Sie sie der Klassendeklaration hinzufügen, und überschreiben Sie die onMapReady()-Methode, um die Karte einzurichten, wenn das GoogleMap-Objekt verfügbar ist:

CurrentPlaceActivity.kt

class CurrentPlaceActivity : AppCompatActivity(), OnMapReadyCallback {

Fügen Sie am Ende der Klasse den folgenden Code hinzu:

CurrentPlaceActivity.kt

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

Für den Callback sind einige Klassenvariablen erforderlich, damit er richtig funktioniert. Fügen Sie direkt nach dem Klassenheader Folgendes hinzu:

CurrentPlaceActivity.kt

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

Fügen Sie dem Companion-Objekt der Klasse den folgenden Code hinzu:

CurrentPlaceActivity.kt

private const val DEFAULT_ZOOM = 15f

Anwendung ausführen

  1. Starten Sie die App.
  2. Tippen Sie auf die Zeile „Aktueller Ort“. Auf dem Bildschirm sollte eine Schaltfläche angezeigt werden.
  3. Tippe auf die Schaltfläche. Wenn Sie dieser App noch keine Berechtigung zur Standortermittlung erteilt haben, sollte eine Berechtigungsanfrage angezeigt werden.
  4. Erteilen Sie der App die Berechtigung, auf den Standort des Geräts zuzugreifen.
  5. Tippen Sie noch einmal auf die Schaltfläche. Die Karte wird angezeigt.

Aktivität „Aktueller Ort“ mit Karte

Abbildung 6: Aktivität „Aktueller Ort“ mit Karte.

Karte mit einem Ort aktualisieren

Fügen Sie am Ende der Klasse den folgenden Code hinzu:

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
    }
}

Sie werden verwendet, um die Daten des Orts zu speichern und zu formatieren.

Fügen Sie am Anfang der Klasse den folgenden Code hinzu, um eine Variable zum Speichern der zurückgegebenen Ortsdaten zu erstellen.

CurrentPlaceActivity.kt

private val likelyPlaces = mutableListOf<LikelyPlace>()

In diesem Schritt wird der Code so geändert, dass dem Nutzer eine Liste mit Orten angezeigt wird und er einen Ort auswählt, der auf der Karte angezeigt werden soll. Alle Daten zu Orten werden in einer Liste auf dem Bildschirm angezeigt.

In der Funktion findCurrentPlace im Block lifecycleScope.launch vor dieser Codezeile

CurrentPlaceActivity.kt

responseView.text = response.prettyPrint()

Fügen Sie den folgenden Code hinzu:

CurrentPlaceActivity.kt

                likelyPlaces.clear()

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

                openPlacesDialog()

Für diesen Code ist eine Konstante für die maximale Anzahl der anzuzeigenden Orte erforderlich.

Fügen Sie dem Companion-Objekt den Code für diese Konstante hinzu.

CurrentPlaceActivity.kt

private const val M_MAX_ENTRIES = 5

Fügen Sie den folgenden Code hinzu, um das Dialogfeld zu erstellen, in dem der Nutzer einen Ort auswählen kann.

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()
    }

Gemäß den Android-Best Practices verweist der Dialog auf eine String-Ressource, die der Ressourcendatei strings.xml im Ordner app/src/main/res/values/ hinzugefügt werden muss.

Fügen Sie strings.xml Folgendes hinzu:

res/values/strings.xml

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

Diese Funktionen rufen dann die Funktion setPlaceOnMap auf, mit der die Kamera bewegt und eine Markierung am ausgewählten Ort platziert wird.

Fügen Sie den folgenden Code hinzu:

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)
        )
    }

Es wird außerdem empfohlen, den Kartenstatus zu speichern und wiederherzustellen.

Überschreiben Sie die Funktion onSaveInstanceState und fügen Sie den folgenden Code hinzu, um den Status zu speichern:

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)
    }

Um den Zustand wiederherzustellen, fügen Sie in onCreate den folgenden Code nach dem Aufruf von setContentView ein:

CurrentPlaceActivity.kt

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

Zum Speichern und Wiederherstellen ist ein Schlüssel erforderlich. Dies ist eine Konstante aus dem Companion-Objekt.

Fügen Sie im Companion-Objektblock Folgendes hinzu:

CurrentPlaceActivity.kt

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

Anwendung ausführen

  1. Starten Sie die App.
  2. Tippen Sie auf die Zeile „Aktueller Ort“. Auf dem Bildschirm sollte eine Schaltfläche angezeigt werden.
  3. Tippe auf die Schaltfläche. Wenn Sie dieser App noch keine Berechtigung zur Standortermittlung erteilt haben, sollte eine Berechtigungsanfrage angezeigt werden.
  4. Erteilen Sie der App die Berechtigung, auf den Standort des Geräts zuzugreifen.
  5. Tippen Sie noch einmal auf die Schaltfläche.
  6. Wählen Sie einen Ort aus, indem Sie darauf tippen. Die Karte wird vergrößert und zentriert und am ausgewählten Ort wird eine Markierung platziert.

Karte mit Markierung am ausgewählten Ort

Abbildung 7: Karte mit Markierung am ausgewählten Ort

12. Glückwunsch

Sie haben erfolgreich eine Android-App mit dem Places SDK for Android erstellt.

Das haben Sie gelernt

Nächste Schritte

  • Weitere Inspirationen finden Sie im android-places-demos GitHub-Repository mit Beispielen und Demos. Sie können es auch forken.
  • Weitere Kotlin-Codelabs für die Entwicklung von Android-Apps mit der Google Maps Platform
  • Helfen Sie uns, die Inhalte zu erstellen, die für Sie am nützlichsten sind, indem Sie die folgende Frage beantworten:

Welche anderen Codelabs würden Sie sich wünschen?

Datenvisualisierung auf Karten Weitere Informationen zum Anpassen des Stils von Karten 3D-Interaktionen in Karten entwickeln

Ist das gewünschte Codelab nicht aufgeführt? Hier können Sie sie mit einem neuen Problem anfordern.