1. Prima di iniziare
Questo codelab ti insegna a integrare Places SDK per Android nella tua app e a utilizzare ciascuna delle funzionalità di Places SDK.
Prerequisiti
- Conoscenza di base di Kotlin e dello sviluppo per Android
Obiettivi didattici
- Come installare Places SDK for Android con le estensioni Kotlin.
- Come caricare i dettagli di un luogo specifico.
- Come aggiungere un widget Place Autocomplete alla tua app.
- Come caricare il luogo attuale in base alla posizione attualmente segnalata del dispositivo.
Che cosa ti serve
Per completare questo codelab, ti serviranno i seguenti account, servizi e strumenti:
- Un Account Google con la fatturazione abilitata.
- Android Studio Bumblebee o versioni successive.
- Google Play Services installato in Android Studio.
- Un dispositivo Android o un emulatore Android che esegue la piattaforma Google APIs basata su Android 8 o versioni successive (vedi Eseguire app sull'emulatore Android per i passaggi di installazione).
2. Configurazione
Per il passaggio di attivazione riportato di seguito, abilita l'API Places e l'SDK Maps per Android.
Configurare Google Maps Platform
Se non hai ancora un account Google Cloud Platform e un progetto con la fatturazione abilitata, consulta la guida Guida introduttiva a Google Maps Platform per creare un account di fatturazione e un progetto.
- Nella console Cloud, fai clic sul menu a discesa del progetto e seleziona il progetto che vuoi utilizzare per questo codelab.
- Abilita le API e gli SDK di Google Maps Platform richiesti per questo codelab in Google Cloud Marketplace. Per farlo, segui i passaggi descritti in questo video o in questa documentazione.
- Genera una chiave API nella pagina Credenziali di Cloud Console. Puoi seguire i passaggi descritti in questo video o in questa documentazione. Tutte le richieste a Google Maps Platform richiedono una chiave API.
3. Avvio rapido
Per iniziare il più rapidamente possibile, scarica il codice iniziale per seguire questo codelab. Puoi passare direttamente alla soluzione, ma se vuoi seguire tutti i passaggi per crearla da solo, continua a leggere.
- Clona il repository se hai installato
git
.
git clone https://github.com/googlemaps/codelab-places-101-android-kotlin.git
In alternativa, fai clic su questo pulsante per scaricare il codice sorgente.
- Dopo aver scaricato il codice, apri il progetto che si trova nella directory
/starter
in Android Studio. Questo progetto include la struttura di base dei file necessaria per completare il codelab. Tutto ciò che ti serve per lavorare si trova nella directory/starter
.
Se vuoi vedere il codice della soluzione completo in esecuzione, puoi visualizzarlo nella directory /solution
.
4. Aggiungere la chiave API al progetto
Questa sezione descrive come archiviare la chiave API in modo che possa essere referenziata in modo sicuro dalla tua app. Non devi archiviare la chiave API nel sistema di controllo delle versioni, pertanto ti consigliamo di archiviarla nel file secrets.properties
, che verrà inserito nella copia locale della directory principale del progetto. Per ulteriori informazioni sul file secrets.properties
, consulta File delle proprietà di Gradle.
Per semplificare questa attività, ti consigliamo di utilizzare il plug-in Secrets Gradle per Android.
Per installare il plug-in Secrets Gradle per Android nel tuo progetto Google Maps:
- In Android Studio, apri il file
build.gradle.kts
obuild.gradle
di primo livello e aggiungi il seguente codice all'elementodependencies
sottobuildscript
.
Se utilizzi build.gradle.kts
, aggiungi:
buildscript {
dependencies {
classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1")
}
}
Se utilizzi build.gradle
, aggiungi:
buildscript {
dependencies {
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
}
}
- Apri il file
build.gradle.kts
obuild.gradle
a livello di modulo e aggiungi il seguente codice all'elementoplugins
.
Se utilizzi build.gradle.kts
, aggiungi:
plugins {
// ...
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
}
Se utilizzi build.gradle
, aggiungi:
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
- Nel file
build.gradle.kts
obuild.gradle
a livello di modulo, assicurati chetargetSdk
ecompileSdk
siano impostati su 34. - Salva il file e sincronizza il progetto con Gradle.
- Apri il file
secrets.properties
nella directory di primo livello, quindi aggiungi il codice seguente. SostituisciYOUR_API_KEY
con la tua chiave API. Memorizza la chiave in questo file perchésecrets.properties
è escluso dal controllo in un sistema di controllo delle versioni.
PLACES_API_KEY=YOUR_API_KEY
- Salva il file.
- Crea il file
local.defaults.properties
nella directory di primo livello, la stessa cartella del filesecrets.properties
, quindi aggiungi il seguente codice.
PLACES_API_KEY=DEFAULT_API_KEY
Lo scopo di questo file è fornire una posizione di backup per la chiave API se il file secrets.properties
non viene trovato, in modo che le build non non vadano a buon fine. Ciò può accadere se cloni l'app da un sistema di controllo delle versioni che omette secrets.properties
e non hai ancora creato un file secrets.properties
in locale per fornire la chiave API.
- Salva il file.
- In Android Studio, apri il file
build.gradle.kts
obuild.gradle
a livello di modulo e modifica la proprietàsecrets
. Se la proprietàsecrets
non esiste, aggiungila.
Modifica le proprietà del plug-in per impostare propertiesFileName
su secrets.properties
, defaultPropertiesFileName
su local.defaults.properties
e qualsiasi altra proprietà.
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. Installa Places SDK per Android
In questa sezione aggiungi Places SDK for Android alle dipendenze della tua app.
- Ora che è possibile accedere alla chiave API all'interno dell'app, aggiungi la dipendenza di Places SDK for Android al file
build.gradle
dell'app.
Modifica il file build.gradle
a livello di app per aggiungere la dipendenza per Places SDK for Android:
build.gradle a livello di app
dependencies {
// Dependency to include Places SDK for Android
implementation 'com.google.android.libraries.places:places:3.4.0'
}
- Esegui l'app.
Ora dovresti vedere un'app con una schermata vuota. Continua a compilare questa schermata con tre demo.
6. Installa Places Android KTX
Per le app Kotlin che utilizzano uno o più SDK Android di Google Maps Platform, le librerie di estensione Kotlin (KTX) ti consentono di sfruttare le funzionalità del linguaggio Kotlin, come coroutine, proprietà/funzioni di estensione e altro ancora. Ogni SDK Google Maps ha una libreria KTX corrispondente, come mostrato di seguito:
In questa attività, utilizza la libreria Places Android KTX per utilizzare le funzionalità specifiche di Kotlin nella tua app.
Aggiungere la dipendenza Places Android KTX
Per sfruttare le funzionalità specifiche di Kotlin, includi la libreria KTX corrispondente per questo SDK nel file build.gradle
a livello di app.
build.gradle
dependencies {
// ...
// Places SDK for Android KTX Library
implementation 'com.google.maps.android:places-ktx:3.1.1'
}
7. Inizializza il client Places
Inizializza Places SDK per l'ambito dell'applicazione
All'interno del file DemoApplication.kt
della cartella app/src/main/java/com/google/codelabs/maps/placesdemo
, inizializza Places SDK per Android. Incolla le righe riportate di seguito alla fine della funzione onCreate
:
// Initialize the SDK with the Google Maps Platform API key
Places.initialize(this, BuildConfig.PLACES_API_KEY)
Quando crei l'app, il plug-in Secrets Gradle per Android rende disponibile la chiave API nel file secrets.properties
come BuildConfig.PLACES_API_KEY
.
Aggiungere il file dell'applicazione al manifest
Poiché hai esteso Application
con DemoApplication
, devi aggiornare il manifest. Aggiungi la proprietà android:name
all'elemento application
nel file AndroidManifest.xml
, che si trova in app/src/main
:
<application
android:name=".DemoApplication"
...
</application>
Questo codice indirizza il manifest dell'applicazione alla classe DemoApplication
nella cartella src/main/java/com/google/codelabs/maps/placesdemo/
.
8. Recupera dettagli luogo
Creare una schermata Dettagli
Un layout activity_details.xml
con un LinearLayout
vuoto è disponibile nella cartella app/src/main/res/layout/
. Compila il layout lineare aggiungendo il seguente codice tra le parentesi <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" />
Questo codice aggiunge un campo di input di testo in cui l'utente può inserire qualsiasi ID luogo o utilizzare il valore predefinito fornito, un pulsante per avviare la richiesta di dettagli sul luogo e una TextView per visualizzare le informazioni della risposta. Le stringhe associate sono definite per te nel file src/main/res/values/strings.xml
.
Creare un'attività Dettagli
- Crea un file
DetailsActivity.kt
nella cartellasrc/main/java/com/google/codelabs/maps/placesdemo/
e associalo al layout appena creato. Incolla questo codice nel file:
@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
}
}
}
- Crea un client Places da utilizzare con questa attività. Incolla questo codice dopo il codice per controllare la chiave API nella funzione
onCreate
.
// Retrieve a PlacesClient (previously initialized - see DemoApplication)
placesClient = Places.createClient(this)
- Dopo aver configurato il client Places, collega un listener di clic al pulsante. Incolla questo codice dopo la creazione del client Places nella funzione
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
}
}
Questo codice recupera l'ID luogo inserito nel campo di input, definisce i campi da richiedere per il luogo, crea un FetchPlaceRequest
, avvia l'attività e ascolta la risposta in caso di esito positivo o negativo. Se la richiesta ha esito positivo, la funzione compila TextView con i dettagli richiesti.
Aggiungere l'attività Dettagli al file manifest
Aggiungi un elemento <activity>
per DetailsActivity
come elemento secondario dell'elemento <application>
nel file AndroidManifest.xml
, che si trova in app/src/main
:
<activity android:name=".DetailsActivity" android:label="@string/details_demo_title" />
Aggiungere l'attività Dettagli al menu della demo
Viene fornito un modulo Demo
vuoto per elencare le demo disponibili nella schermata Home. Ora che hai creato un'attività Dettagli luogo, aggiungila al file Demo.kt
nella cartella src/main/java/com/google/codelabs/maps/placesdemo/
con questo codice:
DETAILS_FRAGMENT_DEMO(
R.string.details_demo_title,
R.string.details_demo_description,
DetailsActivity::class.java
),
Le stringhe associate sono definite nel file src/main/res/values/strings.xml
.
Ispeziona MainActivity.kt
e osserva che crea una ListView popolata dall'iterazione dei contenuti del modulo Demo
. Se l'utente tocca un elemento nell'elenco, il listener di clic apre l'attività associata.
Esegui l'app
- Esegui l'app. Questa volta dovresti vedere un elemento nell'elenco che presenta la demo dei dettagli del luogo.
- Tocca il testo Dettagli del luogo. Dovresti visualizzare la visualizzazione che hai creato con un campo di immissione e un pulsante.
- Tocca il pulsante "SCOPRI DI PIÙ". Se hai utilizzato l'ID luogo predefinito, dovresti visualizzare il nome del luogo, l'indirizzo e le coordinate della mappa, come mostrato nella Figura 1.
Figura 1. Attività Dettagli del luogo con la risposta visualizzata.
9. Aggiungere Place Autocomplete
Creare una schermata di completamento automatico
Un layout activity_autocomplete.xml
con un LinearLayout
vuoto è disponibile nella cartella app/src/main/res/layout/
. Compila il layout lineare aggiungendo questo codice tra le parentesi <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" />
Questo codice aggiunge un widget AutocompleteSupportFragment
e una TextView per visualizzare le informazioni della risposta. Le stringhe associate sono definite nel file src/main/res/values/strings.xml
.
Creare un'attività di completamento automatico
- Crea un file
AutocompleteActivity.kt
nella cartellasrc/main/java/com/google/codelabs/maps/placesdemo/
e definiscilo con questo codice:
@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
}
}
Questo codice associa l'attività alle visualizzazioni e a AutocompleteSupportFramgent
che hai definito nel file di layout.
- Successivamente, definisci cosa succede quando l'utente seleziona una delle previsioni presentate da Place Autocomplete. Aggiungi questo codice alla fine della funzione
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()
}
}
}
Questo codice definisce i campi da richiedere per il luogo, rileva un evento di selezione del luogo e rileva se l'operazione è riuscita o meno. Se la richiesta va a buon fine, la funzione compila il TextView con i dettagli del luogo. Tieni presente che Place Autocomplete restituisce un oggetto Place; non è necessario effettuare una richiesta Places Details separata quando utilizzi il widget Place Autocomplete.
Aggiungere l'attività di completamento automatico al manifest
Aggiungi un elemento <activity>
per AutocompleteActivity
come elemento secondario dell'elemento <application>
nel file AndroidManifest.xml
, che si trova in app/src/main
:
<activity android:name=".AutocompleteActivity" android:label="@string/autocomplete_fragment_demo_title" />
Aggiungi l'attività di completamento automatico al menu demo
Come prima, aggiungi la demo di completamento automatico del luogo alla schermata Home aggiungendola all'elenco nel modulo Demo
. Ora che hai creato un'attività di completamento automatico dei luoghi, aggiungila al file Demo.kt
nella cartella src/main/java/com/google/codelabs/maps/placesdemo/
. Incolla questo codice immediatamente dopo l'elemento DETAILS_FRAGMENT_DEMO
:
AUTOCOMPLETE_FRAGMENT_DEMO(
R.string.autocomplete_fragment_demo_title,
R.string.autocomplete_fragment_demo_description,
AutocompleteActivity::class.java
),
Le stringhe associate sono definite nel file src/main/res/values/strings.xml
.
Esegui l'app
- Esegui l'app. Questa volta dovresti vedere due elementi nell'elenco della schermata Home.
- Tocca la riga di Place Autocomplete. Dovresti visualizzare un popup di input di completamento automatico dei luoghi come mostrato nella Figura 2.
- Inizia a digitare il nome di un luogo. Può essere il nome di un'attività, un indirizzo o una regione geografica. I suggerimenti devono essere visualizzati durante la digitazione.
- Seleziona una delle previsioni. Le previsioni dovrebbero scomparire e la TextView dovrebbe ora mostrare i dettagli sul luogo selezionato, come mostrato nella Figura 3.
Figura 2. Attività di completamento automatico dopo che l'utente tocca il campo di input.
Figura 3. Attività di completamento automatico che mostra i dettagli del luogo dopo che l'utente ha digitato e selezionato "Cascate del Niagara".
10. Ottenere il luogo attuale del dispositivo
Creare una schermata Luogo attuale
Nella cartella app/src/main/res/layout/
è stato fornito un layout activity_current.xml
con un LinearLayout
vuoto. Compila il layout lineare aggiungendo il seguente codice tra le parentesi <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" />
Creare un'attività Luogo attuale
- Crea un file
CurrentPlaceActivity.kt
nella cartellasrc/main/java/com/google/codelabs/maps/placesdemo/
e definiscilo con questo codice:
@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()
}
}
}
Questo codice associa l'attività alle visualizzazioni definite nel file di layout. Inoltre, aggiunge un listener di clic al pulsante per chiamare la funzione checkPermissionThenFindCurrentPlace
quando viene fatto clic sul pulsante.
- Definisci
checkPermissionThenFindCurrentPlace()
per verificare l'autorizzazione di accesso alla posizione esatta e richiedere l'autorizzazione se non è ancora stata concessa. Incolla questo codice dopo la funzioneonCreate
.
/**
* 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
}
- Quando il ramo
else
della funzionecheckPermissionThenFindCurrentPlace
chiamarequestPermissions
, l'app mostra all'utente una finestra di dialogo di richiesta di autorizzazione. Se l'utente utilizza un dispositivo con un sistema operativo precedente ad Android 12, può concedere solo l'autorizzazione di accesso alla posizione esatta (precisa). Se l'utente utilizza un dispositivo con Android 12 o versioni successive, avrà la possibilità di fornire una posizione approssimativa (generica) anziché una posizione esatta (precisa), come mostrato nella Figura 4.
Immagine 4. Quando viene richiesta l'autorizzazione utente su un dispositivo con Android 12 o versioni successive, viene presentata l'opzione di concedere la posizione esatta o approssimativa.
Dopo che l'utente ha risposto alla finestra di dialogo delle autorizzazioni di sistema, il sistema richiama l'implementazione di onRequestPermissionsResult
della tua app. Il sistema passa la risposta dell'utente alla finestra di dialogo delle autorizzazioni, nonché il codice di richiesta che hai definito. Esegui l'override di onRequestPermissionResult
per gestire il codice di richiesta per le autorizzazioni di localizzazione relative a questa attività Current Place incollando il seguente codice sotto 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()
}
- Una volta concessa l'autorizzazione, la funzione
findCurrentPlace
verrà eseguita. Definisci la funzione con questo codice dopo la funzioneonRequestPermissionsResult
.
/**
* 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()
}
}
Questo codice definisce i campi da richiedere per i luoghi probabili, crea un FindCurrentPlaceRequest
, avvia l'attività e compila il TextView con i dettagli richiesti.
Aggiungi l'attività Luogo attuale al manifest
Aggiungi un elemento <activity>
per CurrentPlaceActivity
come elemento secondario dell'elemento <application>
nel file AndroidManifest.xml
, che si trova in app/src/main
:
<activity android:name=".CurrentPlaceActivity" android:label="@string/current_demo_title" />
Aggiungi l'attività Luogo attuale al menu demo
Come prima, aggiungi la demo di Current Place alla schermata Home aggiungendola all'elenco nel modulo Demo
. Ora che hai creato un'attività Luogo attuale, aggiungila al file Demo.kt
nella cartella src/main/java/com/google/codelabs/maps/placesdemo/
. Incolla questo codice immediatamente dopo l'elemento AUTOCOMPLETE_FRAGMENT_DEMO
:
CURRENT_FRAGMENT_DEMO(
R.string.current_demo_title,
R.string.current_demo_description,
CurrentPlaceActivity::class.java
),
Le stringhe associate sono definite nel file src/main/res/values/strings.xml
.
Esegui l'app
- Esegui l'app. Questa volta dovresti vedere tre elementi nell'elenco della schermata Home.
- Tocca la riga Luogo attuale. Dovresti vedere un pulsante sullo schermo.
- Tocca il pulsante. Se non hai mai concesso l'autorizzazione di accesso alla posizione a questa app, dovrebbe essere visualizzata una richiesta di autorizzazione.
- Concedi all'app l'autorizzazione ad accedere alla posizione del dispositivo.
- Tocca di nuovo il pulsante. Questa volta dovrebbe essere visualizzato un elenco di un massimo di 20 luoghi nelle vicinanze e le relative probabilità, come mostrato nella Figura 5.
Figura 5. Mostra le probabili corrispondenze di Luogo attuale per la posizione segnalata del dispositivo.
11. Visualizzare il luogo attuale su una mappa
Aggiungi la dipendenza Map
Nel file build.gradle
a livello di modulo, aggiungi la dipendenza da Google Play Services per l'SDK di Maps per Android.
app/build.gradle
dependencies {
// ...
implementation 'com.google.android.gms:play-services-maps:18.2.0'
}
Aggiorna il file manifest di Android per tenere conto delle mappe
Aggiungi i seguenti elementi meta-data
all'interno dell'elemento application
.
Questi incorporano la versione di Google Play Services con cui è stata compilata l'app e specificano la chiave 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}" />
Aggiungi la chiave API a secrets.properties
Apri il file secrets.properties
nella directory di primo livello, quindi aggiungi il codice seguente. Sostituisci YOUR_API_KEY
con la tua chiave API.
MAPS_API_KEY=YOUR_API_KEY
Apri il file local.defaults.properties
nella directory di primo livello, la stessa cartella del file secrets.properties
, quindi aggiungi il seguente codice.
MAPS_API_KEY=DEFAULT_API_KEY
Controlla la chiave API
In onCreate()
, l'app controllerà la chiave API di Maps e inizializzerà il frammento di supporto di Maps. getMapAsync()
viene utilizzato per registrarsi al richiamo della mappa.
Aggiungi il seguente codice per farlo.
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)
// ...
}
Crea il layout della mappa
- Nella cartella
app/src/main/res/layout/
, crea il file di layoutfragment_map.xml
e inserisci il seguente codice.
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" />
Definisce un SupportMapFragment
che funge da contenitore per la mappa e fornisce l'accesso all'oggetto GoogleMap
.
- Nel layout
activity_current.xml
disponibile nella cartellaapp/src/main/res/layout/
, aggiungi il seguente codice in fondo al layout lineare.
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"/>
Il valore TextView
aggiunto fa riferimento a una nuova risorsa stringa che deve essere creata.
- In
app/src/main/res/values/strings.xml
, aggiungi la seguente risorsa stringa.
res/values/strings.xml
<string name="showing_most_likely_place">Showing most likely place</string>
Poiché sono state aggiunte visualizzazioni aggiuntive per la mappa, l'TextView
che mostra l'elenco dei luoghi deve avere un'altezza impostata in modo che queste visualizzazioni rimangano visibili.
- Aggiungi l'attributo
maxHeight
aTextView
con IDcurrent_response_content
res/layout/activity_current.xml
android:maxHeight="200dp"
Implementa OnMapReadyCallback
Implementa l'interfaccia OnMapReadyCallback
aggiungendola alla dichiarazione della classe e sostituisci il metodo onMapReady()
per configurare la mappa quando l'oggetto GoogleMap
è disponibile:
CurrentPlaceActivity.kt
class CurrentPlaceActivity : AppCompatActivity(), OnMapReadyCallback {
Alla fine della classe, aggiungi il seguente codice:
CurrentPlaceActivity.kt
override fun onMapReady(map: GoogleMap) {
this.map = map
lastKnownLocation?.let { location ->
map.moveCamera(
CameraUpdateFactory.newLatLngZoom(
location,
DEFAULT_ZOOM
)
)
}
}
Il callback richiede alcune variabili di classe per funzionare correttamente. Subito dopo l'intestazione della classe, aggiungi quanto segue:
CurrentPlaceActivity.kt
private var map: GoogleMap? = null
private var lastKnownLocation: LatLng? = null
Aggiungi il seguente codice all'oggetto companion della classe:
CurrentPlaceActivity.kt
private const val DEFAULT_ZOOM = 15f
Esegui l'app
- Esegui l'app.
- Tocca la riga Luogo attuale. Dovresti vedere un pulsante sullo schermo.
- Tocca il pulsante. Se non hai mai concesso l'autorizzazione di accesso alla posizione a questa app, dovrebbe essere visualizzata una richiesta di autorizzazione.
- Concedi all'app l'autorizzazione ad accedere alla posizione del dispositivo.
- Tocca di nuovo il pulsante. Verrà visualizzata la mappa.
Immagine 6. Attività Luogo attuale che mostra la mappa.
Aggiornare la mappa con un luogo
Alla fine della classe, aggiungi il seguente codice:
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
}
}
Questi vengono utilizzati per archiviare e formattare i dati del luogo.
All'inizio della classe, aggiungi il seguente codice per creare una variabile utilizzata per memorizzare i dati del luogo restituito.
CurrentPlaceActivity.kt
private val likelyPlaces = mutableListOf<LikelyPlace>()
In questo passaggio , il codice verrà modificato in modo che all'utente venga mostrato un elenco di luoghi e ne scelga uno da visualizzare sulla mappa. Tutti i dati dei luoghi vengono visualizzati in un elenco sullo schermo.
Nella funzione findCurrentPlace
, nel blocco lifecycleScope.launch
prima di questa riga di codice
CurrentPlaceActivity.kt
responseView.text = response.prettyPrint()
aggiungi il seguente codice:
CurrentPlaceActivity.kt
likelyPlaces.clear()
likelyPlaces.addAll(
response.placeLikelihoods.take(M_MAX_ENTRIES).mapNotNull { placeLikelihood ->
placeLikelihood.toLikelyPlace()
}
)
openPlacesDialog()
Questo codice richiede una costante per il numero massimo di luoghi da mostrare.
Nell'oggetto complementare, aggiungi il codice per la costante.
CurrentPlaceActivity.kt
private const val M_MAX_ENTRIES = 5
Aggiungi il seguente codice che crea la finestra di dialogo che consente all'utente di selezionare un luogo.
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()
}
Seguendo le best practice di Android, la finestra di dialogo fa riferimento a una risorsa stringa che deve essere aggiunta al file di risorse strings.xml
che si trova nella cartella app/src/main/res/values/
.
Aggiungi il seguente codice a strings.xml
:
res/values/strings.xml
<string name="pick_place">Choose a place</string>
Queste funzioni chiamano quindi la funzione setPlaceOnMap
, che sposta la videocamera e posiziona un indicatore nella posizione selezionata.
Aggiungi il codice seguente:
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)
)
}
È inoltre consigliabile salvare e ripristinare lo stato delle mappe.
Per salvare il suo stato, esegui l'override della funzione onSaveInstanceState
e aggiungi il seguente codice:
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)
}
Per ripristinarne lo stato, in onCreate
aggiungi il seguente codice dopo la chiamata a setContentView
:
CurrentPlaceActivity.kt
if (savedInstanceState != null) {
lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
}
Il salvataggio e il ripristino richiedono una chiave, che è una costante dell'oggetto complementare.
Nel blocco dell'oggetto companion, aggiungi quanto segue:
CurrentPlaceActivity.kt
// Key for storing activity state.
private const val KEY_LOCATION = "location"
Esegui l'app
- Esegui l'app.
- Tocca la riga Luogo attuale. Dovresti vedere un pulsante sullo schermo.
- Tocca il pulsante. Se non hai mai concesso l'autorizzazione di accesso alla posizione a questa app, dovrebbe essere visualizzata una richiesta di autorizzazione.
- Concedi all'app l'autorizzazione ad accedere alla posizione del dispositivo.
- Tocca di nuovo il pulsante.
- Scegli un luogo toccandolo. La mappa verrà ingrandita e centrata con un indicatore posizionato nella località selezionata.
Immagine 7. Mappa con indicatore nella posizione selezionata.
12. Complimenti
Hai creato correttamente un'app per Android con Places SDK for Android.
Che cosa hai imparato
- Installazione e configurazione di Places SDK per Android.
- Installazione delle estensioni Kotlin per Places SDK for Android.
- Caricamento di Place Details.
- Aggiunta di Place Autocomplete.
- Recupero di Current Place.
Passaggi successivi
- Esplora o crea un fork del repository GitHub
android-places-demos
di esempi e demo per trovare altra ispirazione. - Scopri di più con altri codelab Kotlin per creare app per Android con Google Maps Platform.
- Aiutaci a creare i contenuti che ritieni più utili rispondendo alla seguente domanda:
Quali altri codelab vorresti vedere?
Il codelab che ti interessa non è presente nell'elenco? Richiedilo con un nuovo problema qui.