1. Zanim zaczniesz
W tym laboratorium dowiesz się, jak zintegrować pakiet SDK Miejsc na Androida z aplikacją i korzystać z poszczególnych funkcji tego pakietu.
Wymagania wstępne
- Podstawowa wiedza o Kotlinie i programowaniu na Androida
Czego się nauczysz
- Jak zainstalować pakiet SDK Miejsc na Androida z rozszerzeniami Kotlin.
- Jak wczytać szczegóły konkretnego miejsca.
- Jak dodać do aplikacji widżet autouzupełniania miejsc.
- Jak wczytać bieżące miejsce na podstawie aktualnej lokalizacji urządzenia.
Czego potrzebujesz
Aby ukończyć to ćwiczenie, potrzebujesz tych kont, usług i narzędzi:
- konto Google z włączonymi płatnościami,
- Android Studio Bumblebee lub nowsze.
- Usługi Google Play zainstalowane w Android Studio.
- Urządzenie z Androidem lub emulator Androida z platformą interfejsów API Google opartą na Androidzie 8 lub nowszym (instrukcje instalacji znajdziesz w artykule Uruchamianie aplikacji na emulatorze Androida).
2. Konfiguracja
W kroku włączania poniżej włącz interfejs Places API i pakiet SDK Map Google na Androida.
Konfigurowanie Google Maps Platform
Jeśli nie masz jeszcze konta Google Cloud Platform i projektu z włączonymi płatnościami, zapoznaj się z przewodnikiem Pierwsze kroki z Google Maps Platform, aby utworzyć konto rozliczeniowe i projekt.
- W konsoli Google Cloud kliknij menu projektu i wybierz projekt, którego chcesz użyć w tym samouczku.
- Włącz interfejsy API i pakiety SDK Google Maps Platform wymagane w tym samouczku w Google Cloud Marketplace. Aby to zrobić, wykonaj czynności opisane w tym filmie lub tej dokumentacji.
- Wygeneruj klucz interfejsu API na stronie Dane logowania w konsoli Cloud. Możesz wykonać czynności opisane w tym filmie lub tej dokumentacji. Wszystkie żądania wysyłane do Google Maps Platform wymagają klucza interfejsu API.
3. Szybki start
Aby jak najszybciej rozpocząć pracę, pobierz kod początkowy, który pomoże Ci wykonać to ćwiczenie z programowania. Możesz przejść od razu do rozwiązania, ale jeśli chcesz wykonać wszystkie czynności, aby samodzielnie je utworzyć, czytaj dalej.
- Sklonuj repozytorium, jeśli masz zainstalowany program
git
.
git clone https://github.com/googlemaps/codelab-places-101-android-kotlin.git
Możesz też kliknąć ten przycisk, aby pobrać kod źródłowy.
- Po pobraniu kodu otwórz projekt znajdujący się w katalogu
/starter
w Android Studio. Ten projekt zawiera podstawową strukturę plików, która będzie potrzebna do ukończenia ćwiczenia. Wszystko, czego potrzebujesz do pracy, znajduje się w katalogu/starter
.
Jeśli chcesz zobaczyć działający pełny kod rozwiązania, możesz wyświetlić gotowy kod w katalogu /solution
.
4. Dodawanie klucza interfejsu API do projektu
W tej sekcji opisujemy, jak przechowywać klucz interfejsu API, aby aplikacja mogła się do niego bezpiecznie odwoływać. Nie należy umieszczać klucza interfejsu API w systemie kontroli wersji, dlatego zalecamy przechowywanie go w pliku secrets.properties
, który zostanie umieszczony w lokalnej kopii katalogu głównego projektu. Więcej informacji o pliku secrets.properties
znajdziesz w artykule Pliki właściwości Gradle.
Aby uprościć to zadanie, zalecamy użycie wtyczki Gradle obiektów tajnych na Androida.
Aby zainstalować wtyczkę Gradle obiektów tajnych na Androida w projekcie Google Maps:
- W Android Studio otwórz plik najwyższego poziomu
build.gradle.kts
lubbuild.gradle
i dodaj ten kod do elementudependencies
w sekcjibuildscript
.
Jeśli używasz build.gradle.kts
, dodaj:
buildscript {
dependencies {
classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1")
}
}
Jeśli używasz build.gradle
, dodaj:
buildscript {
dependencies {
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
}
}
- Otwórz plik
build.gradle.kts
lubbuild.gradle
na poziomie modułu i dodaj ten kod do elementuplugins
.
Jeśli używasz build.gradle.kts
, dodaj:
plugins {
// ...
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
}
Jeśli używasz build.gradle
, dodaj:
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
- W pliku
build.gradle.kts
lubbuild.gradle
na poziomie modułu sprawdź, czy wartościtargetSdk
icompileSdk
są ustawione na 34. - Zapisz plik i zsynchronizuj projekt z Gradle.
- Otwórz plik
secrets.properties
w katalogu najwyższego poziomu i dodaj ten kod: ZastąpYOUR_API_KEY
swoim kluczem interfejsu API. Przechowuj klucz w tym pliku, ponieważsecrets.properties
jest wykluczony z systemu kontroli wersji.
PLACES_API_KEY=YOUR_API_KEY
- Zapisz plik.
- Utwórz plik
local.defaults.properties
w katalogu najwyższego poziomu, tym samym, w którym znajduje się pliksecrets.properties
, a następnie dodaj ten kod.
PLACES_API_KEY=DEFAULT_API_KEY
Ten plik służy jako lokalizacja kopii zapasowej klucza interfejsu API, jeśli plik secrets.properties
nie zostanie znaleziony, aby kompilacje nie kończyły się niepowodzeniem. Może się to zdarzyć, jeśli sklonujesz aplikację z systemu kontroli wersji, który pomija plik secrets.properties
, a nie utworzysz jeszcze lokalnie pliku secrets.properties
, aby podać klucz interfejsu API.
- Zapisz plik.
- W Android Studio otwórz plik
build.gradle.kts
lubbuild.gradle
na poziomie modułu i zmień właściwośćsecrets
. Jeśli właściwośćsecrets
nie istnieje, dodaj ją.
Edytuj właściwości wtyczki, aby ustawić propertiesFileName
na secrets.properties
, defaultPropertiesFileName
na local.defaults.properties
i inne właściwości.
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. Instalowanie pakietu SDK Miejsc na Androida
W tej sekcji dodasz pakiet SDK Miejsc na Androida do zależności aplikacji.
- Teraz, gdy klucz interfejsu API jest dostępny w aplikacji, dodaj zależność pakietu Places SDK na Androida do pliku
build.gradle
aplikacji.
Zmodyfikuj plik build.gradle
na poziomie aplikacji, aby dodać zależność pakietu SDK Miejsc na Androida:
build.gradle na poziomie aplikacji
dependencies {
// Dependency to include Places SDK for Android
implementation 'com.google.android.libraries.places:places:3.4.0'
}
- Uruchom aplikację.
Powinna być widoczna aplikacja z pustym ekranem. Wypełnij ten ekran 3 wersjami demonstracyjnymi.
6. Instalowanie biblioteki Places Android KTX
W przypadku aplikacji w Kotlinie, które korzystają z co najmniej jednego pakietu SDK Google Maps Platform na Androida, biblioteki rozszerzeń Kotlin (KTX) umożliwiają korzystanie z funkcji języka Kotlin, takich jak korutyny, właściwości i funkcje rozszerzeń itp. Każdy pakiet SDK Map Google ma odpowiednią bibliotekę KTX, jak pokazano poniżej:
W tym zadaniu użyjesz biblioteki Places Android KTX, aby w aplikacji korzystać z funkcji języka Kotlin.
Dodawanie zależności Places Android KTX
Aby korzystać z funkcji specyficznych dla języka Kotlin, w pliku build.gradle
na poziomie aplikacji uwzględnij odpowiednią bibliotekę KTX dla tego pakietu SDK.
build.gradle
dependencies {
// ...
// Places SDK for Android KTX Library
implementation 'com.google.maps.android:places-ktx:3.1.1'
}
7. Inicjowanie klienta Places
Inicjowanie pakietu SDK Miejsc w zakresie aplikacji
W pliku DemoApplication.kt
w folderze app/src/main/java/com/google/codelabs/maps/placesdemo
zainicjuj pakiet SDK Miejsc na Androida. Na końcu funkcji onCreate
wklej te wiersze:
// Initialize the SDK with the Google Maps Platform API key
Places.initialize(this, BuildConfig.PLACES_API_KEY)
Gdy tworzysz aplikację, wtyczka Gradle obiektów tajnych na Androida udostępnia klucz interfejsu API w pliku secrets.properties
jako BuildConfig.PLACES_API_KEY
.
Dodawanie pliku aplikacji do manifestu
Ponieważ masz rozszerzenie Application
z DemoApplication
, musisz zaktualizować plik manifestu. Dodaj właściwość android:name
do elementu application
w pliku AndroidManifest.xml
znajdującym się w app/src/main
:
<application
android:name=".DemoApplication"
...
</application>
Ten kod wskazuje plik manifestu aplikacji na klasę DemoApplication
w folderze src/main/java/com/google/codelabs/maps/placesdemo/
.
8. Pobieranie szczegółów miejsca
Tworzenie ekranu szczegółów
Układ activity_details.xml
z pustym LinearLayout
jest dostępny w folderze app/src/main/res/layout/
. Wypełnij układ liniowy, dodając ten kod między nawiasami <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" />
Ten kod dodaje pole wprowadzania tekstu, w którym użytkownik może wpisać dowolny identyfikator miejsca lub użyć podanej wartości domyślnej, przycisk do zainicjowania żądania szczegółów miejsca oraz element TextView do wyświetlania informacji z odpowiedzi. Powiązane ciągi znaków są zdefiniowane w pliku src/main/res/values/strings.xml
.
Tworzenie aktywności Szczegóły
- Utwórz plik
DetailsActivity.kt
w folderzesrc/main/java/com/google/codelabs/maps/placesdemo/
i powiąż go z utworzonym przed chwilą układem. Wklej do pliku ten kod:
@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
}
}
}
- Utwórz klienta Miejsc do użycia w tej aktywności. Wklej ten kod po kodzie, który sprawdza klucz interfejsu API w funkcji
onCreate
.
// Retrieve a PlacesClient (previously initialized - see DemoApplication)
placesClient = Places.createClient(this)
- Po skonfigurowaniu klienta Places dołącz do przycisku odbiornik kliknięć. Wklej ten kod po utworzeniu klienta Places w funkcji
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
}
}
Ten kod pobiera identyfikator miejsca wpisany w polu wejściowym, określa pola, o które należy poprosić w przypadku miejsca, tworzy FetchPlaceRequest
, inicjuje zadanie i nasłuchuje, czy zakończy się ono sukcesem, czy nie. Jeśli żądanie zostanie wykonane, funkcja wypełni element TextView żądanymi szczegółami.
Dodawanie aktywności Szczegóły do pliku manifestu
Dodaj element <activity>
dla DetailsActivity
jako element podrzędny elementu <application>
w pliku AndroidManifest.xml
znajdującym się w app/src/main
:
<activity android:name=".DetailsActivity" android:label="@string/details_demo_title" />
Dodawanie aktywności Szczegóły do menu wersji demonstracyjnej
Pusty moduł Demo
służy do wyświetlania listy dostępnych wersji demonstracyjnych na ekranie głównym. Po utworzeniu działania Place Details dodaj je do pliku Demo.kt
w folderze src/main/java/com/google/codelabs/maps/placesdemo/
za pomocą tego kodu:
DETAILS_FRAGMENT_DEMO(
R.string.details_demo_title,
R.string.details_demo_description,
DetailsActivity::class.java
),
Powiązane ciągi znaków są zdefiniowane w pliku src/main/res/values/strings.xml
.
Sprawdź MainActivity.kt
i zobacz, że tworzy on element ListView, który jest wypełniany przez iterację zawartości modułu Demo
. Jeśli użytkownik kliknie element na liście, detektor kliknięć otworzy powiązaną aktywność.
Uruchamianie aplikacji
- Uruchom aplikację. Tym razem na liście powinien pojawić się jeden element przedstawiający wersję demonstracyjną szczegółów miejsca.
- Kliknij tekst Szczegóły miejsca. Powinien wyświetlić się utworzony widok z polem wprowadzania i przyciskiem.
- Kliknij przycisk „ZOBACZ SZCZEGÓŁY”. Jeśli używasz domyślnego identyfikatora miejsca, powinny się wyświetlić nazwa miejsca, adres i współrzędne mapy, jak pokazano na ilustracji 1.
Rysunek 1. Aktywność Szczegóły miejsca z wyświetloną odpowiedzią.
9. Dodawanie autouzupełniania miejsc
Tworzenie ekranu autouzupełniania
Układ activity_autocomplete.xml
z pustym LinearLayout
znajduje się w folderze app/src/main/res/layout/
. Wypełnij układ liniowy, dodając ten kod między nawiasami <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" />
Ten kod dodaje widżet AutocompleteSupportFragment
i element TextView do wyświetlania informacji z odpowiedzi. Powiązane ciągi znaków są zdefiniowane w pliku src/main/res/values/strings.xml
.
Tworzenie aktywności autouzupełniania
- Utwórz plik
AutocompleteActivity.kt
w folderzesrc/main/java/com/google/codelabs/maps/placesdemo/
i zdefiniuj go za pomocą tego kodu:
@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
}
}
Ten kod wiąże aktywność z widokami i AutocompleteSupportFramgent
zdefiniowanymi w pliku układu.
- Następnie określ, co się stanie, gdy użytkownik wybierze jedną z prognoz wyświetlanych przez autouzupełnianie miejsc. Dodaj ten kod na końcu funkcji
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()
}
}
}
Ten kod określa, o które pola należy poprosić w przypadku miejsca, nasłuchuje zdarzenia wyboru miejsca oraz nasłuchuje powodzenia lub niepowodzenia. Jeśli prośba zostanie rozpatrzona pozytywnie, funkcja wypełni widok TextView szczegółami miejsca. Pamiętaj, że autouzupełnianie miejsc zwraca obiekt Place. Podczas korzystania z widżetu autouzupełniania miejsc nie musisz wysyłać osobnego żądania szczegółów miejsca.
Dodawanie aktywności autouzupełniania do pliku manifestu
Dodaj element <activity>
dla AutocompleteActivity
jako element podrzędny elementu <application>
w pliku AndroidManifest.xml
znajdującym się w app/src/main
:
<activity android:name=".AutocompleteActivity" android:label="@string/autocomplete_fragment_demo_title" />
Dodawanie działania Autouzupełnianie do menu demonstracyjnego
Tak jak wcześniej dodaj wersję demonstracyjną autouzupełniania miejsc do ekranu głównego, dołączając ją do listy w module Demo
. Po utworzeniu aktywności autouzupełniania miejsca dodaj ją do pliku Demo.kt
w folderze src/main/java/com/google/codelabs/maps/placesdemo/
. Wklej ten kod bezpośrednio po elemencie DETAILS_FRAGMENT_DEMO
:
AUTOCOMPLETE_FRAGMENT_DEMO(
R.string.autocomplete_fragment_demo_title,
R.string.autocomplete_fragment_demo_description,
AutocompleteActivity::class.java
),
Powiązane ciągi znaków są zdefiniowane w pliku src/main/res/values/strings.xml
.
Uruchamianie aplikacji
- Uruchom aplikację. Tym razem na liście na ekranie głównym powinny być widoczne 2 elementy.
- Kliknij wiersz Autouzupełnianie miejsc. Powinno pojawić się okienko wprowadzania autouzupełniania miejsc, jak pokazano na rysunku 2.
- Zacznij wpisywać nazwę miejsca. Może to być nazwa placówki, adres lub region geograficzny. Podpowiedzi powinny pojawiać się podczas pisania.
- Wybierz jedną z prognoz. Prognozy powinny zniknąć, a w obszarze TextView powinny się teraz wyświetlać szczegóły wybranego miejsca, jak pokazano na rysunku 3.
Rysunek 2. Automatyczne uzupełnianie po kliknięciu pola wprowadzania przez użytkownika.
Rysunek 3. Aktywność autouzupełniania wyświetlająca szczegóły miejsca po wpisaniu i wybraniu przez użytkownika hasła „Niagara Falls”.
10. Pobieranie Aktualnego miejsca urządzenia
Tworzenie ekranu bieżącego miejsca
W folderze app/src/main/res/layout/
znajduje się układ activity_current.xml
z pustym LinearLayout
. Wypełnij układ liniowy, dodając ten kod między nawiasami <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" />
Tworzenie aktywności Aktualne miejsce
- Utwórz plik
CurrentPlaceActivity.kt
w folderzesrc/main/java/com/google/codelabs/maps/placesdemo/
i zdefiniuj go za pomocą tego kodu:
@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()
}
}
}
Ten kod wiąże aktywność z widokami zdefiniowanymi w pliku układu. Dodaje też do przycisku detektor kliknięć, który po kliknięciu przycisku wywołuje funkcję checkPermissionThenFindCurrentPlace
.
- Zdefiniuj
checkPermissionThenFindCurrentPlace()
, aby sprawdzić, czy aplikacja ma uprawnienia do dokładnej lokalizacji, i poproś o nie, jeśli nie zostały jeszcze przyznane. Wklej ten kod po funkcjionCreate
.
/**
* 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
}
- Gdy gałąź
else
funkcjicheckPermissionThenFindCurrentPlace
wywoła funkcjęrequestPermissions
, aplikacja wyświetli użytkownikowi okno z prośbą o uprawnienia. Jeśli użytkownik korzysta z urządzenia z systemem operacyjnym starszym niż Android 12, może przyznać tylko uprawnienia do dokładnej lokalizacji. Jeśli użytkownik korzysta z urządzenia z Androidem 12 lub nowszym, będzie miał możliwość podania przybliżonej (ogólnej) lokalizacji zamiast dokładnej, jak pokazano na rysunku 4.
Rysunek 4. Gdy użytkownik poprosi o uprawnienia na urządzeniu z Androidem 12 lub nowszym, będzie mógł przyznać dostęp do dokładnej lub przybliżonej lokalizacji.
Gdy użytkownik odpowie na okno uprawnień systemowych, system wywoła implementację onRequestPermissionsResult
w Twojej aplikacji. System przekazuje odpowiedź użytkownika na okno dialogowe z prośbą o uprawnienia oraz zdefiniowany przez Ciebie kod żądania. Zastąp onRequestPermissionResult
, aby obsłużyć kod żądania uprawnień do lokalizacji związanych z tą aktywnością bieżącego miejsca, wklejając poniższy kod pod 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()
}
- Gdy to zrobisz, funkcja
findCurrentPlace
zostanie uruchomiona. Zdefiniuj funkcję za pomocą tego kodu po funkcjionRequestPermissionsResult
.
/**
* 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()
}
}
Ten kod określa pola, o które należy poprosić w przypadku prawdopodobnych miejsc, tworzy FindCurrentPlaceRequest
, inicjuje zadanie i wypełnia element TextView żądanymi szczegółami.
Dodaj aktywność Bieżące miejsce do pliku manifestu
Dodaj element <activity>
dla CurrentPlaceActivity
jako element podrzędny elementu <application>
w pliku AndroidManifest.xml
znajdującym się w app/src/main
:
<activity android:name=".CurrentPlaceActivity" android:label="@string/current_demo_title" />
Dodaj aktywność Bieżące miejsce do menu wersji demonstracyjnej
Podobnie jak wcześniej dodaj wersję demonstracyjną bieżącego miejsca do ekranu głównego, dołączając ją do listy w module Demo
. Po utworzeniu aktywności Bieżące miejsce dodaj ją do pliku Demo.kt
w folderze src/main/java/com/google/codelabs/maps/placesdemo/
. Wklej ten kod bezpośrednio po elemencie AUTOCOMPLETE_FRAGMENT_DEMO
:
CURRENT_FRAGMENT_DEMO(
R.string.current_demo_title,
R.string.current_demo_description,
CurrentPlaceActivity::class.java
),
Powiązane ciągi znaków są zdefiniowane w pliku src/main/res/values/strings.xml
.
Uruchamianie aplikacji
- Uruchom aplikację. Tym razem na liście na ekranie głównym powinny być widoczne 3 elementy.
- Kliknij wiersz Aktualne miejsce. Na ekranie powinien pojawić się przycisk.
- Naciśnij przycisk. Jeśli wcześniej nie przyznano tej aplikacji uprawnień do lokalizacji, powinna pojawić się prośba o uprawnienia.
- Zezwól aplikacji na dostęp do lokalizacji urządzenia.
- Ponownie kliknij przycisk. Tym razem powinna się pojawić lista maksymalnie 20 miejsc w pobliżu i ich prawdopodobieństwa, jak pokazano na rysunku 5.
Rysunek 5. Wyświetlanie prawdopodobnych dopasowań bieżącego miejsca do zgłoszonej lokalizacji urządzenia.
11. Wyświetlanie bieżącego miejsca na mapie
Dodaj zależność Mapy
W pliku build.gradle
na poziomie modułu dodaj zależność Usług Google Play dla pakietu Maps SDK na Androida.
app/build.gradle
dependencies {
// ...
implementation 'com.google.android.gms:play-services-maps:18.2.0'
}
Zaktualizuj plik manifestu Androida, aby uwzględnić mapy
Dodaj te elementy meta-data
w elemencie application
.
Zawierają one wersję Usług Google Play, z którą skompilowano aplikację, i określają klucz interfejsu 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}" />
Dodaj klucz interfejsu API do secrets.properties
Otwórz plik secrets.properties
w katalogu najwyższego poziomu i dodaj ten kod: Zastąp YOUR_API_KEY
swoim kluczem interfejsu API.
MAPS_API_KEY=YOUR_API_KEY
Otwórz plik local.defaults.properties
w katalogu najwyższego poziomu, czyli w tym samym folderze co plik secrets.properties
, a następnie dodaj ten kod.
MAPS_API_KEY=DEFAULT_API_KEY
Sprawdź klucz interfejsu API
W onCreate()
aplikacja sprawdzi klucz interfejsu API Map Google i zainicjuje fragment obsługi map. getMapAsync()
służy do rejestrowania wywołania zwrotnego z mapy.
Aby to zrobić, dodaj ten kod.
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)
// ...
}
Tworzenie układu mapy
- W folderze
app/src/main/res/layout/
utwórz plik układufragment_map.xml
i wypełnij go tym kodem.
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" />
Określa element SupportMapFragment
, który będzie działać jako kontener mapy i zapewni dostęp do obiektu GoogleMap
.
- W układzie
activity_current.xml
dostępnym w folderzeapp/src/main/res/layout/
dodaj ten kod na dole układu liniowego.
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"/>
Dodany znak TextView
odwołuje się do nowego zasobu w postaci ciągu znaków, który należy utworzyć.
- W pliku
app/src/main/res/values/strings.xml
dodaj ten zasób ciągu znaków.
res/values/strings.xml
<string name="showing_most_likely_place">Showing most likely place</string>
Ponieważ do mapy dodano dodatkowe widoki, element TextView
wyświetlający listę miejsc musi mieć ustawioną wysokość, aby te widoki pozostały widoczne.
- Dodaj atrybut
maxHeight
do elementuTextView
o identyfikatorzecurrent_response_content
.
res/layout/activity_current.xml
android:maxHeight="200dp"
Wdróż funkcję OnMapReadyCallback
.
Zaimplementuj interfejs OnMapReadyCallback
, dodając go do deklaracji klasy, i zastąp metodę onMapReady()
, aby skonfigurować mapę, gdy obiekt GoogleMap
będzie dostępny:
CurrentPlaceActivity.kt
class CurrentPlaceActivity : AppCompatActivity(), OnMapReadyCallback {
Na końcu klasy dodaj ten kod:
CurrentPlaceActivity.kt
override fun onMapReady(map: GoogleMap) {
this.map = map
lastKnownLocation?.let { location ->
map.moveCamera(
CameraUpdateFactory.newLatLngZoom(
location,
DEFAULT_ZOOM
)
)
}
}
Aby działać prawidłowo, wywołanie zwrotne wymaga pewnych zmiennych klasy. Bezpośrednio po nagłówku klasy dodaj ten kod:
CurrentPlaceActivity.kt
private var map: GoogleMap? = null
private var lastKnownLocation: LatLng? = null
Dodaj ten kod do obiektu towarzyszącego klasy:
CurrentPlaceActivity.kt
private const val DEFAULT_ZOOM = 15f
Uruchamianie aplikacji
- Uruchom aplikację.
- Kliknij wiersz Aktualne miejsce. Na ekranie powinien pojawić się przycisk.
- Naciśnij przycisk. Jeśli wcześniej nie przyznano tej aplikacji uprawnień do lokalizacji, powinna pojawić się prośba o uprawnienia.
- Zezwól aplikacji na dostęp do lokalizacji urządzenia.
- Ponownie kliknij przycisk. Wyświetli się mapa.
Rysunek 6. Aktywność w sekcji Bieżące miejsce z wyświetloną mapą.
Aktualizowanie mapy o miejsce
Na końcu klasy dodaj ten kod:
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
}
}
Służą one do przechowywania i formatowania danych miejsca.
Na początku klasy dodaj ten kod, aby utworzyć zmienną służącą do przechowywania zwróconych danych o miejscu.
CurrentPlaceActivity.kt
private val likelyPlaces = mutableListOf<LikelyPlace>()
W tym kroku zmienimy kod, aby użytkownikowi wyświetlała się lista miejsc, z której będzie mógł wybrać jedno do wyświetlenia na mapie. Wszystkie dane o miejscach są wyświetlane na ekranie w postaci listy.
W funkcji findCurrentPlace
w bloku lifecycleScope.launch
przed tym wierszem kodu
CurrentPlaceActivity.kt
responseView.text = response.prettyPrint()
dodaj ten kod:
CurrentPlaceActivity.kt
likelyPlaces.clear()
likelyPlaces.addAll(
response.placeLikelihoods.take(M_MAX_ENTRIES).mapNotNull { placeLikelihood ->
placeLikelihood.toLikelyPlace()
}
)
openPlacesDialog()
Ten kod wymaga stałej wartości określającej maksymalną liczbę miejsc do wyświetlania.
W obiekcie towarzyszącym dodaj kod tej stałej.
CurrentPlaceActivity.kt
private const val M_MAX_ENTRIES = 5
Dodaj ten kod, który tworzy okno umożliwiające użytkownikowi wybranie miejsca.
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()
}
Zgodnie ze sprawdzonymi metodami dotyczącymi Androida okno odwołuje się do zasobu w postaci ciągu znaków, który należy dodać do strings.xml
pliku zasobów znajdującego się w folderze app/src/main/res/values/
.
Dodać te boty do pokoju „strings.xml
”?
res/values/strings.xml
<string name="pick_place">Choose a place</string>
Funkcje te wywołują następnie funkcję setPlaceOnMap
, która przesuwa kamerę i umieszcza znacznik w wybranej lokalizacji.
Dodaj ten kod:
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)
)
}
Zalecamy też zapisywanie i przywracanie stanu map.
Aby zapisać stan, zastąp funkcję onSaveInstanceState
i dodaj ten kod:
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)
}
Aby przywrócić jego stan, w onCreate
dodaj ten kod po wywołaniu setContentView
:
CurrentPlaceActivity.kt
if (savedInstanceState != null) {
lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
}
Zapisywanie i przywracanie wymaga klucza, który jest stałą z obiektu towarzyszącego.
W bloku obiektu towarzyszącego dodaj te elementy:
CurrentPlaceActivity.kt
// Key for storing activity state.
private const val KEY_LOCATION = "location"
Uruchamianie aplikacji
- Uruchom aplikację.
- Kliknij wiersz Aktualne miejsce. Na ekranie powinien pojawić się przycisk.
- Naciśnij przycisk. Jeśli wcześniej nie przyznano tej aplikacji uprawnień do lokalizacji, powinna pojawić się prośba o uprawnienia.
- Zezwól aplikacji na dostęp do lokalizacji urządzenia.
- Ponownie kliknij przycisk.
- Wybierz miejsce, klikając je. Mapa zostanie powiększona i wyśrodkowana, a w wybranej lokalizacji pojawi się znacznik.
Rysunek 7. Mapa ze znacznikiem w wybranej lokalizacji.
12. Gratulacje
Udało Ci się utworzyć aplikację na Androida za pomocą pakietu SDK Miejsc na Androida.
Czego się dowiedziałeś
- Instalowanie i konfigurowanie pakietu SDK Miejsc na Androida.
- Instalowanie rozszerzeń Kotlin dla pakietu SDK Miejsc na Androida.
- Wczytuję szczegóły miejsca.
- Dodawanie autouzupełniania miejsc.
- Pobieranie Aktualnego miejsca.
Co dalej?
- Aby znaleźć więcej inspiracji, przejrzyj lub utwórz rozwidlenie
android-places-demos
repozytorium GitHub z przykładami i wersjami demonstracyjnymi. - Skorzystaj z większej liczby ćwiczeń z programowania w Kotlinie, aby tworzyć aplikacje na Androida za pomocą Google Maps Platform.
- Pomóż nam tworzyć treści, które będą dla Ciebie najbardziej przydatne, i odpowiedz na to pytanie:
Jakie inne codelaby chcesz zobaczyć?
Nie widzisz interesującego Cię laboratorium? Zgłoś problem tutaj