1. Antes de começar
Este codelab ensina como integrar o SDK do Places para Android ao seu app e usar cada um dos recursos dele.
Pré-requisitos
- Conhecimento básico de desenvolvimento Android e Kotlin
O que você vai aprender
- Como instalar o SDK do Places para Android com extensões Kotlin.
- Como carregar detalhes de um lugar específico.
- Como adicionar um widget do Place Autocomplete ao app.
- Como carregar o local atual com base no lugar informado pelo dispositivo no momento.
Pré-requisitos
Para concluir este codelab, você vai precisar das seguintes contas, serviços e ferramentas:
- Uma Conta do Google com faturamento ativado.
- Android Studio Bumblebee ou versão mais recente.
- Google Play Services instalado no Android Studio.
- Um dispositivo Android ou um Android Emulator que execute a plataforma de APIs do Google com base no Android 8 ou versões mais recentes (consulte Executar apps no Android Emulator para ver as etapas de instalação).
2. Começar a configuração
Para a etapa abaixo, ative a API Places e o SDK do Maps para Android.
Configurar a Plataforma Google Maps
Caso você ainda não tenha uma conta do Google Cloud Platform e um projeto com faturamento ativado, veja como criá-los no guia da Plataforma Google Maps.
- No Console do Cloud, clique no menu suspenso do projeto e selecione o projeto que você quer usar neste codelab.
- Ative as APIs e os SDKs da Plataforma Google Maps necessários para este codelab no Google Cloud Marketplace. Para fazer isso, siga as etapas descritas neste vídeo ou nesta documentação.
- Gere uma chave de API na página Credenciais do Console do Cloud. Siga as etapas indicadas neste vídeo ou nesta documentação. Todas as solicitações à Plataforma Google Maps exigem uma chave de API.
3. Início rápido
Para começar o mais rápido possível e acompanhar este codelab, faça o download do código inicial. Se preferir, você pode ir direto para a solução, mas continue lendo se quiser desenvolver por conta própria.
- Clone o repositório se você tiver o
git
instalado.
git clone https://github.com/googlemaps/codelab-places-101-android-kotlin.git
Se preferir, clique neste botão para fazer o download do código-fonte.
- Depois de fazer o download do código, abra o projeto no diretório
/starter
do Android Studio. Esse projeto inclui a estrutura de arquivos básica necessária para concluir o codelab. Tudo o que você precisa para trabalhar está no diretório/starter
.
Se você quiser ver o código completo da solução em execução, veja o código concluído no diretório /solution
.
4. Adicionar sua chave de API ao projeto
Nesta seção, descrevemos como armazenar sua chave de API para que ela possa ser referenciada com segurança pelo seu app. Não faça a verificação dela no sistema de controle de versões. Recomendamos armazená-la no arquivo secrets.properties
, que será colocado na sua cópia local do diretório raiz do projeto. Para saber mais sobre o arquivo secrets.properties
, consulte Arquivos de propriedades do Gradle.
Para otimizar essa tarefa, recomendamos usar o plug-in Secrets Gradle para Android.
Para instalar esse plug-in no seu projeto do Google Maps, faça o seguinte:
- No Android Studio, abra o arquivo de nível superior
build.gradle.kts
oubuild.gradle
e adicione o seguinte código ao elementodependencies
embuildscript
.
Se você estiver usando build.gradle.kts
, adicione:
buildscript {
dependencies {
classpath("com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1")
}
}
Se você estiver usando build.gradle
, adicione:
buildscript {
dependencies {
classpath "com.google.android.libraries.mapsplatform.secrets-gradle-plugin:secrets-gradle-plugin:2.0.1"
}
}
- Abra o arquivo
build.gradle.kts
oubuild.gradle
no nível do módulo e adicione o seguinte código ao elementoplugins
.
Se você estiver usando build.gradle.kts
, adicione:
plugins {
// ...
id("com.google.android.libraries.mapsplatform.secrets-gradle-plugin")
}
Se você estiver usando build.gradle
, adicione:
plugins {
// ...
id 'com.google.android.libraries.mapsplatform.secrets-gradle-plugin'
}
- No arquivo
build.gradle.kts
oubuild.gradle
no nível do módulo, definatargetSdk
ecompileSdk
como 34. - Salve o arquivo e sincronize seu projeto com o Gradle.
- Abra o arquivo
secrets.properties
no diretório de nível superior e adicione o código a seguir. SubstituaYOUR_API_KEY
pela sua chave de API. Armazene sua chave nesse arquivo porquesecrets.properties
não é verificado em um sistema de controle de versões.
PLACES_API_KEY=YOUR_API_KEY
- Salve o arquivo.
- Crie o arquivo
local.defaults.properties
no seu diretório de nível superior, na mesma pasta que o arquivosecrets.properties
, e depois adicione o seguinte código.
PLACES_API_KEY=DEFAULT_API_KEY
O objetivo desse arquivo é oferecer um local de backup para a chave da API se o arquivo secrets.properties
não for encontrado, para que os builds não apresentem falha. Isso pode acontecer se você clonar o app de um sistema de controle de versões que omite secrets.properties
e ainda não tiver criado um arquivo secrets.properties
localmente para fornecer sua chave de API.
- Salve o arquivo.
- No Android Studio, abra o arquivo
build.gradle.kts
oubuild.gradle
no nível do módulo e edite a propriedadesecrets
. Se a propriedadesecrets
não existir, adicione-a.
Edite as propriedades do plug-in para definir propertiesFileName
como secrets.properties
, defaultPropertiesFileName
como local.defaults.properties
e outras propriedades.
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. Instalar o SDK do Places para Android
Nesta seção, você adiciona o SDK do Places para Android às dependências do seu app.
- Agora que sua chave de API pode ser acessada no app, adicione a dependência do SDK do Places para Android ao arquivo
build.gradle
do seu app.
Modifique o arquivo build.gradle
no nível do app para adicionar a dependência do SDK do Places para Android:
build.gradle no nível do app
dependencies {
// Dependency to include Places SDK for Android
implementation 'com.google.android.libraries.places:places:3.4.0'
}
- Execute o app.
Você vai ver um app com uma tela vazia. Continue a preencher a tela com três demonstrações.
6. Instalar o Android KTX do Places
Para apps Kotlin que usam um ou mais SDKs do Android da Plataforma Google Maps, as bibliotecas de extensão Kotlin (KTX) permitem aproveitar recursos da linguagem Kotlin, como corrotinas, propriedades/funções de extensão e muito mais. Cada SDK do Google Maps tem uma biblioteca KTX correspondente, como mostrado abaixo:
Nesta tarefa, utilize a biblioteca Places do Android KTX para usar recursos da linguagem específica do Kotlin no seu app.
Adicionar a dependência do Android KTX do Places
Para aproveitar os recursos específicos do Kotlin, inclua a biblioteca KTX correspondente para esse SDK no arquivo build.gradle
no nível do app.
build.gradle
dependencies {
// ...
// Places SDK for Android KTX Library
implementation 'com.google.maps.android:places-ktx:3.1.1'
}
7. Inicializar o cliente do Places
Inicializar o SDK do Places para o escopo do aplicativo
No arquivo DemoApplication.kt
da pasta app/src/main/java/com/google/codelabs/maps/placesdemo
, inicialize o SDK do Places para Android. Cole as linhas abaixo no fim da função onCreate
:
// Initialize the SDK with the Google Maps Platform API key
Places.initialize(this, BuildConfig.PLACES_API_KEY)
Quando você cria o app, o plug-in de secrets do Gradle para Android (em inglês) disponibiliza a chave de API no seu arquivo secrets.properties
como BuildConfig.PLACES_API_KEY
.
Adicionar o arquivo de aplicativo ao manifesto
Como você estendeu o Application
com DemoApplication
, é necessário atualizar o manifesto. Adicione a propriedade android:name
ao elemento application
no arquivo AndroidManifest.xml
, localizado em app/src/main
:
<application
android:name=".DemoApplication"
...
</application>
Esse código aponta o manifesto do aplicativo para a classe DemoApplication
na pasta src/main/java/com/google/codelabs/maps/placesdemo/
.
8. Buscar Place Details
Criar a tela "Detalhes"
Um layout activity_details.xml
com um LinearLayout
vazio está disponível na pasta app/src/main/res/layout/
. Preencha o layout linear adicionando o seguinte código entre os colchetes <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" />
Esse código adiciona um campo de entrada de texto em que o usuário pode inserir qualquer ID de local ou usar o padrão fornecido, um botão para iniciar a solicitação do Place Details e um TextView para exibir as informações da resposta. As strings associadas são definidas para você no arquivo src/main/res/values/strings.xml
.
Criar a atividade "Detalhes"
- Crie um arquivo
DetailsActivity.kt
na pastasrc/main/java/com/google/codelabs/maps/placesdemo/
e associe-o ao layout que você acabou de criar. Cole este código no arquivo:
@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
}
}
}
- Crie um cliente do Places para usar com essa atividade. Cole esse código após o código para verificar a chave de API na função
onCreate
.
// Retrieve a PlacesClient (previously initialized - see DemoApplication)
placesClient = Places.createClient(this)
- Depois que o cliente do Places estiver configurado, anexe um listener de cliques ao botão. Cole este código após a criação do cliente do Places na função
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
}
}
Esse código recupera o ID de lugar que foi inserido no campo de entrada, define quais campos vão ser solicitados, cria um FetchPlaceRequest
, inicia a tarefa e detecta um sucesso ou uma falha. Quando a solicitação é bem-sucedida, a função preenche a TextView com os detalhes solicitados.
Adicionar a atividade "Detalhes" ao manifesto
Adicione um elemento <activity>
para o DetailsActivity
como filho do elemento <application>
no arquivo AndroidManifest.xml
, localizado em app/src/main
:
<activity android:name=".DetailsActivity" android:label="@string/details_demo_title" />
Adicionar a atividade "Detalhes" ao menu de demonstração
Um módulo Demo
vazio é fornecido para listar as demonstrações disponíveis na tela inicial. Agora que você criou uma atividade do Place Details, adicione-a ao arquivo Demo.kt
na pasta src/main/java/com/google/codelabs/maps/placesdemo/
com este código:
DETAILS_FRAGMENT_DEMO(
R.string.details_demo_title,
R.string.details_demo_description,
DetailsActivity::class.java
),
As strings associadas são definidas no arquivo src/main/res/values/strings.xml
.
Inspecione o MainActivity.kt
e observe que ele cria um ListView preenchido pela iteração do conteúdo do módulo Demo
. Quando o usuário toca em um item da lista, o listener de cliques abre a atividade associada.
Executar o app
- Execute o app. Desta vez, você vai ver um item na lista com a demonstração do Place Details.
- Toque no texto do Place Details. Você vai ter acesso à visualização criada com um botão e um campo de entrada.
- Toque no botão "VER DETALHES". Se você usou o ID de lugar padrão, vai ver o nome, o endereço e as coordenadas do mapa, como mostrado na Figura 1.
Figura 1. Atividade do Place Details com resposta exibida.
9. Adicionar Place Autocomplete
Criar uma tela de preenchimento automático
Um layout activity_autocomplete.xml
com um LinearLayout
vazio foi fornecido na pasta app/src/main/res/layout/
. Preencha o layout linear adicionando este código entre os colchetes <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" />
Esse código adiciona um widget AutocompleteSupportFragment
e um TextView para exibir as informações da resposta. As strings associadas são definidas no arquivo src/main/res/values/strings.xml
.
Criar uma atividade de preenchimento automático
- Crie um arquivo
AutocompleteActivity.kt
na pastasrc/main/java/com/google/codelabs/maps/placesdemo/
e defina-o com este código:
@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
}
}
Esse código associa a atividade às visualizações e ao AutocompleteSupportFramgent
que você definiu no arquivo de layout.
- Em seguida, defina o que acontece quando o usuário seleciona uma das previsões apresentadas pelo Place Autocomplete. Adicione este código ao fim da função
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()
}
}
}
Esse código define os campos que são solicitados para o lugar e detecta um evento de seleção de lugar, além de sucesso ou falha. Quando a solicitação é bem-sucedida, a função preenche a TextView com os detalhes do lugar. O Place Autocomplete retorna um objeto do Place. Não é necessário fazer outra solicitação do Place Details ao usar o widget do Place Autocomplete.
Adicionar a atividade de preenchimento automático ao manifesto
Adicione um elemento <activity>
para o AutocompleteActivity
como filho do elemento <application>
no arquivo AndroidManifest.xml
, localizado em app/src/main
:
<activity android:name=".AutocompleteActivity" android:label="@string/autocomplete_fragment_demo_title" />
Adicionar a atividade de preenchimento automático ao menu de demonstração
Assim como antes, adicione a demonstração do Place Autocomplete à tela inicial anexando-a à lista no módulo Demo
. Agora que você criou uma atividade do Place Autocomplete, adicione-a ao arquivo Demo.kt
na pasta src/main/java/com/google/codelabs/maps/placesdemo/
. Cole este código imediatamente após o item DETAILS_FRAGMENT_DEMO
:
AUTOCOMPLETE_FRAGMENT_DEMO(
R.string.autocomplete_fragment_demo_title,
R.string.autocomplete_fragment_demo_description,
AutocompleteActivity::class.java
),
As strings associadas são definidas no arquivo src/main/res/values/strings.xml
.
Executar o app
- Execute o app. Desta vez, você vai ver dois itens na lista da tela inicial.
- Toque na linha do Place Autocomplete. Uma entrada do Place Autocomplete é exibida como mostrado na Figura 2.
- Comece a digitar o nome de um lugar. Pode ser o nome de um estabelecimento, um endereço ou uma região geográfica. As previsões são apresentadas enquanto você digita.
- Selecione uma das previsões. As previsões vão desaparecer, e o TextView vai exibir os detalhes do lugar selecionado, como mostrado na Figura 3.
Figura 2. Atividade de preenchimento automático depois que o usuário toca no campo de entrada.
Figura 3. Atividade de preenchimento automático que exibe o Place Details depois que o usuário digita e seleciona "Niagara Falls".
10. Acessar o local atual do dispositivo
Criar uma tela do local atual
Um layout activity_current.xml
com um LinearLayout
vazio foi fornecido na pasta app/src/main/res/layout/
. Preencha o layout linear adicionando o seguinte código entre os colchetes <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" />
Criar uma atividade do local atual
- Crie um arquivo
CurrentPlaceActivity.kt
na pastasrc/main/java/com/google/codelabs/maps/placesdemo/
e defina-o com este código:
@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()
}
}
}
Esse código associa a atividade às visualizações que você definiu no arquivo de layout. Ele também adiciona um listener de clique ao botão para chamar a função checkPermissionThenFindCurrentPlace
quando o botão é clicado.
- Defina
checkPermissionThenFindCurrentPlace()
para verificar a permissão de localização exata e solicite a permissão, se ela ainda não tiver sido concedida. Cole este código após a funçãoonCreate
.
/**
* 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 a ramificação
else
da funçãocheckPermissionThenFindCurrentPlace
chamarequestPermissions
, o app apresenta uma caixa de diálogo de solicitação de permissão ao usuário. Quando o usuário executa um dispositivo com um SO anterior ao Android 12, ele só pode conceder permissões de localização precisas. Quando o usuário usa um dispositivo com o Android 12 ou mais recente, ele pode informar uma localização aproximada, em vez de um lugar exato, como mostrado na Figura 4.
Figura 4. Solicitar permissão do usuário em um dispositivo com o Android 12 ou mais recente permite conceder uma localização aproximada ou precisa.
Depois que o usuário responde à caixa de diálogo de permissões, o sistema invoca a implementação do app de onRequestPermissionsResult
. O sistema transmite a resposta do usuário à caixa de diálogo de permissão, além do código de solicitação definido. Substitua onRequestPermissionResult
para processar o código de solicitação das permissões de localização relacionadas a essa atividade do local atual colando o seguinte código abaixo de 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()
}
- Quando a permissão for concedida, a função
findCurrentPlace
vai ser executada. Defina a função com esse código após a funçãoonRequestPermissionsResult
.
/**
* 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()
}
}
Esse código define quais campos vão ser solicitados para os possíveis lugares, cria um FindCurrentPlaceRequest
, inicia a tarefa e preenche o TextView com os detalhes solicitados.
Adicionar a atividade do local atual ao manifesto
Adicione um elemento <activity>
para o CurrentPlaceActivity
como filho do elemento <application>
no arquivo AndroidManifest.xml
, localizado em app/src/main
:
<activity android:name=".CurrentPlaceActivity" android:label="@string/current_demo_title" />
Adicionar a atividade do local atual ao menu de demonstração
Assim como antes, adicione a demonstração do local atual à tela inicial anexando-a à lista no módulo Demo
. Agora que você criou uma atividade do local atual, adicione-a ao arquivo Demo.kt
na pasta src/main/java/com/google/codelabs/maps/placesdemo/
. Cole este código imediatamente após o item AUTOCOMPLETE_FRAGMENT_DEMO
:
CURRENT_FRAGMENT_DEMO(
R.string.current_demo_title,
R.string.current_demo_description,
CurrentPlaceActivity::class.java
),
As strings associadas são definidas no arquivo src/main/res/values/strings.xml
.
Executar o app
- Execute o app. Desta vez, você vai ver três itens na lista da tela inicial.
- Toque na linha do local atual. Você vai ver um botão na tela.
- Toque no botão. Se você nunca concedeu permissão de localização a este app, uma solicitação de permissão vai ser exibida.
- Conceda permissão ao app para acessar a localização do dispositivo.
- Toque no botão novamente. Desta vez, uma lista de até 20 lugares por perto e as probabilidades deles serão exibidas, como mostrado na Figura 5.
Figura 5. Apresentação de possíveis correspondências com locais atuais para o local informado pelo dispositivo.
11. Mostrar o lugar atual em um mapa
Adicionar a dependência do mapa
No arquivo build.gradle
no nível do módulo, adicione a dependência do Google Play Services no SDK do Maps para Android.
app/build.gradle
dependencies {
// ...
implementation 'com.google.android.gms:play-services-maps:18.2.0'
}
Atualizar o manifesto do Android para incluir mapas
Adicione os seguintes elementos meta-data
ao elemento application
.
Eles incorporam a versão do Google Play Services com que o app foi compilado e especificam sua chave de 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}" />
Adicione a chave de API a secrets.properties
Abra o arquivo secrets.properties
no diretório de nível superior e adicione o código a seguir. Substitua YOUR_API_KEY
pela sua chave de API.
MAPS_API_KEY=YOUR_API_KEY
Abra o arquivo local.defaults.properties
no diretório de nível superior, na mesma pasta que o arquivo secrets.properties
, e adicione o seguinte código.
MAPS_API_KEY=DEFAULT_API_KEY
Verificar a chave de API
Em onCreate()
, o app vai verificar a chave de API do Maps e inicializar o fragmento de suporte do Maps. getMapAsync()
é usado para registrar o callback do mapa.
Adicione o código a seguir para fazer isso.
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)
// ...
}
Criar o layout do mapa
- Na pasta
app/src/main/res/layout/
, crie o arquivo de layoutfragment_map.xml
e preencha-o com o seguinte código.
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" />
Isso define um SupportMapFragment
para atuar como um contêiner do mapa e dar acesso ao objeto GoogleMap
.
- No layout
activity_current.xml
disponível na pastaapp/src/main/res/layout/
, adicione o seguinte código à parte de baixo do layout linear.
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"/>
O TextView
adicionado faz referência a um novo recurso de string que precisa ser criado.
- Em
app/src/main/res/values/strings.xml
, adicione o seguinte recurso de string.
res/values/strings.xml
<string name="showing_most_likely_place">Showing most likely place</string>
Como outras visualizações foram adicionadas ao mapa, a TextView
que mostra a lista de lugares precisa ter a altura definida para que essas visualizações permaneçam visíveis.
- Adicione o atributo
maxHeight
aoTextView
com IDcurrent_response_content
res/layout/activity_current.xml
android:maxHeight="200dp"
Implementar o OnMapReadyCallback
Implemente a interface OnMapReadyCallback
adicionando-a à declaração de classe e substitua o método onMapReady()
para configurar o mapa quando o objeto GoogleMap
estiver disponível:
CurrentPlaceActivity.kt
class CurrentPlaceActivity : AppCompatActivity(), OnMapReadyCallback {
No final da classe, adicione o seguinte código:
CurrentPlaceActivity.kt
override fun onMapReady(map: GoogleMap) {
this.map = map
lastKnownLocation?.let { location ->
map.moveCamera(
CameraUpdateFactory.newLatLngZoom(
location,
DEFAULT_ZOOM
)
)
}
}
O callback exige algumas variáveis de classe para funcionar corretamente. Imediatamente após o cabeçalho da classe, adicione o seguinte:
CurrentPlaceActivity.kt
private var map: GoogleMap? = null
private var lastKnownLocation: LatLng? = null
Adicione o seguinte código ao objeto complementar da classe:
CurrentPlaceActivity.kt
private const val DEFAULT_ZOOM = 15f
Executar o app
- Execute o app.
- Toque na linha do local atual. Você vai ver um botão na tela.
- Toque no botão. Se você nunca concedeu permissão de localização a este app, uma solicitação de permissão vai ser exibida.
- Conceda permissão ao app para acessar a localização do dispositivo.
- Toque no botão novamente. O mapa vai aparecer.
Figura 6. Atividade do local atual mostrando o mapa.
Atualizar o mapa com um lugar
No final da classe, adicione o seguinte código:
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ão usados para armazenar e formatar os dados do lugar.
No início da classe, adicione o seguinte código para criar uma variável usada para armazenar os dados do lugar retornado.
CurrentPlaceActivity.kt
private val likelyPlaces = mutableListOf<LikelyPlace>()
Nesta etapa , o código será alterado para que o usuário receba uma lista de lugares e escolha um para mostrar no mapa. Todos os dados do Google Maps são mostrados em uma lista na tela.
Na função findCurrentPlace
, no bloco lifecycleScope.launch
antes desta linha de código
CurrentPlaceActivity.kt
responseView.text = response.prettyPrint()
Adicione o seguinte código:
CurrentPlaceActivity.kt
likelyPlaces.clear()
likelyPlaces.addAll(
response.placeLikelihoods.take(M_MAX_ENTRIES).mapNotNull { placeLikelihood ->
placeLikelihood.toLikelyPlace()
}
)
openPlacesDialog()
Esse código exige uma constante para o número máximo de lugares a serem mostrados.
No objeto complementar, adicione o código dessa constante.
CurrentPlaceActivity.kt
private const val M_MAX_ENTRIES = 5
Adicione o código a seguir, que cria a caixa de diálogo para o usuário selecionar um lugar.
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()
}
Seguindo as práticas recomendadas do Android, a caixa de diálogo faz referência a um recurso de string que precisa ser adicionado ao arquivo de recursos strings.xml
localizado na pasta app/src/main/res/values/
.
Adicione o seguinte a strings.xml
:
res/values/strings.xml
<string name="pick_place">Choose a place</string>
Essas funções chamam a função setPlaceOnMap
, que move a câmera e coloca um marcador no local selecionado.
Adicione o seguinte código:
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)
)
}
Também é recomendável salvar e restaurar o estado dos mapas.
Para salvar o estado, substitua a função onSaveInstanceState
e adicione o seguinte código:
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)
}
Para restaurar o estado, em onCreate
, adicione o seguinte código após a chamada para setContentView
:
CurrentPlaceActivity.kt
if (savedInstanceState != null) {
lastKnownLocation = savedInstanceState.getParcelable(KEY_LOCATION)
}
Salvar e restaurar requer uma chave, que é uma constante do objeto complementar.
No bloco de objeto complementar, adicione o seguinte:
CurrentPlaceActivity.kt
// Key for storing activity state.
private const val KEY_LOCATION = "location"
Executar o app
- Execute o app.
- Toque na linha do local atual. Você vai ver um botão na tela.
- Toque no botão. Se você nunca concedeu permissão de localização a este app, uma solicitação de permissão vai ser exibida.
- Conceda permissão ao app para acessar a localização do dispositivo.
- Toque no botão novamente.
- Toque em um lugar para escolher. O mapa vai ser ampliado e centralizado com um marcador no local selecionado.
Figura 7. Mapa com marcador no local selecionado.
12. Parabéns
Você criou um app Android com o SDK do Places para Android.
O que você aprendeu
- Como instalar e configurar o SDK do Places para Android.
- Como instalar extensões Kotlin para o SDK do Places para Android.
- Como carregar o Place Details.
- Adicionar Place Autocomplete.
- Como acessar o Local atual.
Qual é a próxima etapa?
- Explore ou crie uma bifurcação do repositório
android-places-demos
de amostras e demonstrações do GitHub para ter mais inspiração. - Aprenda com mais codelabs do Kotlin para criar apps Android com a Plataforma Google Maps.
- Ajude-nos a criar conteúdo útil respondendo à seguinte pergunta:
Quais outros codelabs você quer ver?
O codelab que você quer ver não está listado? Solicite-o aqui.