Este tutorial faz parte do curso Android avançado em Kotlin. Você obterá o máximo valor deste curso se trabalhar com os tutoriais em sequência, mas isso não é obrigatório. Todos os tutoriais do curso estão listados na página de destino Android avançado em Kotlin.
Construir aplicativos com o Google Maps permite adicionar recursos ao seu aplicativo, como imagens de satélite, controles robustos de IU para mapas, rastreamento de localização e marcadores de localização. Você pode agregar valor ao Google Maps padrão mostrando informações de seu próprio conjunto de dados, como a localização de áreas conhecidas de pesca ou escalada. Você também pode criar jogos onde o jogador explora o mundo físico, como em uma caça ao tesouro, ou mesmo jogos de realidade aumentada.
Nesta lição, você criará um aplicativo do Google Maps chamado Wander que exibe mapas personalizados e mostra a localização do usuário.
Se você encontrar algum problema (bugs de código, erros gramaticais, palavras pouco claras etc.) ao trabalhar neste tutorial, relate o problema por meio do link Informar um erro no canto esquerdo inferior do tutorial.
Wander
, que tem um mapa do Google embutido.Neste tutorial, você cria o aplicativo Wander
, que exibe um mapa do Google com estilo personalizado. O aplicativo Wander
permite que você coloque marcadores em locais, adicione sobreposições e veja sua localização em tempo real.
A API do Google Maps requer uma chave de API. Para obter a chave de API, você registra seu projeto no Console de API do Google. A chave API está vinculada a um certificado digital que vincula o aplicativo ao seu autor. Para obter mais informações sobre como usar certificados digitais e assinar seu aplicativo, consulte Assinar seu aplicativo.
Nesta prática, você usa a chave API para o certificado de depuração. O certificado de depuração não é seguro por design, conforme descrito em Assine sua compilação de depuração. Os aplicativos Android publicados que usam a API do Google Maps exigem uma segunda chave de API: a chave para o certificado de liberação. Para obter mais informações sobre como obter um certificado de liberação, consulte Obter chave API.
O Android Studio inclui um modelo de atividade do Google Maps, que gera um código de modelo útil. O código do modelo inclui um arquivo google_maps_api.xml
contendo um link que simplifica a obtenção de uma chave de API.
google_maps_api.xml— Use este arquivo de configuração para manter sua chave de API. O modelo gera dois arquivos google_maps_api.xml
: um para depuração e outro para lançamento. O arquivo da chave API para o certificado de depuração está localizado em src/debug/res/values
. O arquivo da chave API do certificado de liberação está localizado em src/release/res/values
. Neste tutorial, você usa apenas o certificado de depuração.
activity_maps.xml— Este arquivo de layout contém um único fragmento que preenche a tela inteira. A classe SupportMapFragment
é uma subclasse da classe Fragment
. Um SupportMapFragment
é a maneira mais simples de colocar um mapa em um aplicativo. É um embrulho em torno da vista de um mapa para tratar automaticamente com as necessidades de ciclo de vida necessárias.
Você pode incluir SupportMapFragment
em um arquivo de layout usando uma etiqueta<fragment>
em qualquer ViewGroup
, com um name
adicional atributo.
android:name="com.google.android.gms.maps.SupportMapFragment"
MapsActivity.java—O arquivo MapsActivity.kt
instancia o SupportMapFragment
no método onCreate()
e usa o método da classe getMapAsync
()
para inicializar automaticamente o sistema de mapas e a vista. A atividade que contém o SupportMapFragment
deve implementar a interface OnMapReadyCallback
e o método onMapReady()
dessa interface. O método onMapReady()
é chamado quando o mapa é carregado.
google_maps_api.xml
. AIza
. google_maps_api.xml
, cole a chave na string google_maps_key
onde está escrito YOUR_KEY_HERE
.MapsActivity
tem um lateinit
var
chamado mMap
, que é do tipo GoogleMap
. Para seguir as convenções de nomenclatura Kotlin, mude o nome de mMap
para map
.
MapsActivity
, clique com o botão direito em mMap
e clique em Refactor/Rename...map
. Observe como todas as referências a mMap
na função onMapReady()
também mudam para map
.
O Google Maps inclui vários tipos de mapa: normal, híbrido, satélite, terreno e "nenhum" (para nenhum mapa).
Mapa Normal |
Mapa de satélite |
Mapa Híbrido |
Mapa de Terreno |
Cada tipo de mapa fornece diferentes tipos de informações. Por exemplo, ao usar mapas para navegação em um carro, é útil ver os nomes das ruas, portanto, você pode usar a opção normal. Quando você está caminhando, o mapa do terreno pode ser útil para decidir o quanto mais você precisa escalar para chegar ao topo.
Nesta tarefa você:
Nesta etapa, você adiciona uma barra de aplicativos com um menu de opções que permite ao usuário alterar o tipo de mapa.
res
e selecione New > Android Resource File.map_options
. Escolha Menu para o tipo de recurso. Clique em OK.
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/normal_map"
android:title="@string/normal_map"
app:showAsAction="never" />
<item
android:id="@+id/hybrid_map"
android:title="@string/hybrid_map"
app:showAsAction="never" />
<item
android:id="@+id/satellite_map"
android:title="@string/satellite_map"
app:showAsAction="never" />
<item
android:id="@+id/terrain_map"
android:title="@string/terrain_map"
app:showAsAction="never" />
</menu>
strings.xml
, adicione recursos para os atributos title
a fim de resolver os erros.<resources>
...
<string name="normal_map">Normal Map</string>
<string name="hybrid_map">Hybrid Map</string>
<string name="satellite_map">Satellite Map</string>
<string name="terrain_map">Terrain Map</string>
<string name="lat_long_snippet">Lat: %1$.5f, Long: %2$.5f</string>
<string name="dropped_pin">Dropped Pin</string>
<string name="poi">poi</string>
</resources>
MapsActivity
, substitua o método onCreateOptionsMenu()
e aumente o menu do arquivo de recurso map_options
.override fun onCreateOptionsMenu(menu: Menu?): Boolean {
val inflater = menuInflater
inflater.inflate(R.menu.map_options, menu)
return true
}
MapsActivity.kt
, substitua o método onOptionsItemSelected()
. Altere o tipo de mapa usando constantes de tipo de mapa para refletir a seleção do usuário.override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
// Change the map type based on the user's selection.
R.id.normal_map -> {
map.mapType = GoogleMap.MAP_TYPE_NORMAL
true
}
R.id.hybrid_map -> {
map.mapType = GoogleMap.MAP_TYPE_HYBRID
true
}
R.id.satellite_map -> {
map.mapType = GoogleMap.MAP_TYPE_SATELLITE
true
}
R.id.terrain_map -> {
map.mapType = GoogleMap.MAP_TYPE_TERRAIN
true
}
else -> super.onOptionsItemSelected(item)
}
Por padrão, o retorno de chamada onMapReady()
inclui o código que coloca um marcador em Sydney, Austrália, onde o Google Maps foi criado. O retorno de chamada padrão também anima o mapa para deslocar até Sydney.
Nesta tarefa, você faz com que a câmera do mapa se mova para sua casa, amplie para um nível que você especificar e coloque um marcador lá.
onMapReady()
, remova o código que coloca o marcador em Sydney e move a câmera. Esta é a aparência do seu método agora.override fun onMapReady(googleMap: GoogleMap) {
map = googleMap
}
val latitude = 37.422160
val longitude = -122.084270
LatLng
chamado homeLatLng
. No objeto homeLatLng
, passe os valores que você acabou de criar.val homeLatLng = LatLng(latitude, longitude)
val
para quanto você deseja aumentar o zoom no mapa. Use o nível de zoom 15f.val zoomLevel = 15f
O nível de zoom controla o quão ampliado você está no mapa. A lista a seguir dá uma ideia de qual nível de detalhe cada nível de zoom mostra:
1
: Mundo5
: Massa terrestre / continente10
: Cidade15
: Ruas20
: EdifícioshomeLatLng
chamando a função moveCamera()
no objeto map
e passe um objeto CameraUpdate
usando CameraUpdateFactory.newLatLngZoom()
. Passe o objeto homeLatLng
e o zoomLevel
.map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
homeLatLng
.map.addMarker(MarkerOptions().position(homeLatLng))
Seu método final deve ser semelhante a este.
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap
//These coordinates represent the latitude and longitude of the Googleplex.
val latitude = 37.422160
val longitude = -122.084270
val zoomLevel = 15f
val homeLatLng = LatLng(latitude, longitude)
map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
map.addMarker(MarkerOptions().position(homeLatLng))
}
Nesta etapa, você adiciona um marcador quando o usuário toca e mantém um local no mapa.
MapsActivity
chamado setMapLongClick()
que leva um GoogleMap
como argumento. setOnMapLongClickListener
ao objeto do mapa.private fun setMapLongClick(map:GoogleMap) {
map.setOnMapLongClickListener { }
}
setOnMapLongClickListener()
, chame o método addMarker()
. Passe um novo objeto MarkerOptions
com a posição definida para o LatLng
passado.private fun setMapLongClick(map: GoogleMap) {
map.setOnMapLongClickListener { latLng ->
map.addMarker(
MarkerOptions()
.position(latLng)
)
}
}
onMapReady()
, chame setMapLongClick()
com map
.override fun onMapReady(googleMap: GoogleMap) {
...
setMapLongClick(map)
}
Nesta etapa, você adiciona uma InfoWindow
que exibe as coordenadas do marcador quando o marcador é tocado.
setMapLongClick()setOnMapLongClickListener()
, crie um val
para o snippet
. Um trecho é um texto adicional exibido abaixo do título. No seu caso, o snippet exibe a latitude e longitude de um marcador.private fun setMapLongClick(map: GoogleMap) {
map.setOnMapLongClickListener { latLng ->
// A Snippet is Additional text that's displayed below the title.
val snippet = String.format(
Locale.getDefault(),
"Lat: %1$.5f, Long: %2$.5f",
latLng.latitude,
latLng.longitude
)
map.addMarker(
MarkerOptions()
.position(latLng)
)
}
}
addMarker()
, defina o title
do marcador como "Pino caído" usando um R.string.
dropped_pin
recurso de string.snippet
do marcador como snippet
. Abaixo está a função concluída.
private fun setMapLongClick(map: GoogleMap) {
map.setOnMapLongClickListener { latLng ->
// A Snippet is Additional text that's displayed below the title.
val snippet = String.format(
Locale.getDefault(),
"Lat: %1$.5f, Long: %2$.5f",
latLng.latitude,
latLng.longitude
)
map.addMarker(
MarkerOptions()
.position(latLng)
.title(getString(R.string.dropped_pin))
.snippet(snippet)
)
}
}
Por padrão, os pontos de interesse (POIs) aparecem no mapa junto com seus ícones correspondentes. Os POIs incluem parques, escolas, edifícios governamentais e muito mais. Quando o tipo de mapa é definido como normal
, POIs comerciais também aparecem no mapa. Os POIs de negócios representam empresas como lojas, restaurantes e hotéis.
Nesta etapa, você adiciona um GoogleMap.OnPoiClickListener
ao mapa. Este ouvinte de clique coloca um marcador no mapa imediatamente quando o usuário clica em um POI. O ouvinte de cliques também exibe uma janela de informações que contém o nome do POI.
MapsActivity
chamado setPoiClick()
que leva um GoogleMap
como argumento. setPoiClick()
, defina um OnPoiClickListener
no GoogleMap
passado.private fun setPoiClick(map: GoogleMap) {
map.setOnPoiClickListener { poi ->
}
}
onPoiClick()
, crie um val poiMarker
para o marcador. map.addMarker()
com MarkerOptions
definindo o title
para o nome do POI. private fun setPoiClick(map: GoogleMap) {
map.setOnPoiClickListener { poi ->
val poiMarker = map.addMarker(
MarkerOptions()
.position(poi.latLng)
.title(poi.name)
)
}
}
setOnPoiClickListener()
, chame showInfoWindow()
em poiMarker
para mostrar imediatamente a janela de informações.poiMarker.showInfoWindow()
Sua função final setPoiClick()
deve ser semelhante a esta.
private fun setPoiClick(map: GoogleMap) {
map.setOnPoiClickListener { poi ->
val poiMarker = map.addMarker(
MarkerOptions()
.position(poi.latLng)
.title(poi.name)
)
poiMarker.showInfoWindow()
}
}
onMapReady()
, chame setPoiClick()
e passe o map
.override fun onMapReady(googleMap: GoogleMap) {
...
setPoiClick(map)
}
Você pode personalizar o Google Maps de várias maneiras, dando ao seu mapa uma aparência única.
Você pode personalizar um objeto MapFragment
usando os atributos XML disponíveis, da mesma forma que personalizaria qualquer outro fragmento. No entanto, nesta etapa, você personaliza a aparência do conteúdo do MapFragment
, usando métodos no objeto GoogleMap
.
Para criar um estilo customizado para seu mapa, você gera um arquivo JSON que especifica como os recursos no mapa são exibidos. Você não precisa criar esse arquivo JSON manualmente: o Google fornece o Assistente de estilo, que gera o JSON para você depois que você estiliza visualmente seu mapa. Nesta tarefa, você estiliza o mapa para "retro", o que significa que o mapa usa cores vintage e você adicionará estradas coloridas.
res
, crie um diretório de recursos e nomeie-o como raw
. Use os recursos de diretório raw
como o código JSON.res/raw
chamado map_style.json
.MapsActivity
, crie uma variável de classe TAG
acima do método onCreate()
. Isso será usado para fins de registro.private val TAG = MapsActivity::class.java.simpleName
MapsActivity
, crie uma função setMapStyle()
que leva em um GoogleMap.setMapStyle()
, adicione um bloco try{}
.try{}
, crie um val success
para o sucesso do estilo. (Você adicionará o bloco de captura abaixo.)try{}
, defina o estilo JSON para o mapa, chame setMapStyle()
no GoogleMap
objeto. Passe um objeto MapStyleOptions
, que carrega o arquivo JSON. success
. O método setMapStyle()
retorna um booleano indicando o status de sucesso da análise do arquivo de estilo e definição do estilo. private fun setMapStyle(map: GoogleMap) {
try {
// Customize the styling of the base map using a JSON object defined
// in a raw resource file.
val success = map.setMapStyle(
MapStyleOptions.loadRawResourceStyle(
this,
R.raw.map_style
)
)
}
}
success
sendo falso. Se o estilo não for bem-sucedido, imprima um registro informando que a análise falhou.private fun setMapStyle(map: GoogleMap) {
try {
...
if (!success) {
Log.e(TAG, "Style parsing failed.")
}
}
}
catch{}
para tratar com a situação de um arquivo de estilo ausente. No bloco catch
, se o arquivo não puder ser carregado, lance uma Resources.NotFoundException
. private fun setMapStyle(map: GoogleMap) {
try {
...
} catch (e: Resources.NotFoundException) {
Log.e(TAG, "Can't find style. Error: ", e)
}
}
O método concluído deve ser semelhante ao código abaixo.
private fun setMapStyle(map: GoogleMap) {
try {
// Customize the styling of the base map using a JSON object defined
// in a raw resource file.
val success = map.setMapStyle(
MapStyleOptions.loadRawResourceStyle(
this,
R.raw.map_style
)
)
if (!success) {
Log.e(TAG, "Style parsing failed.")
}
} catch (e: Resources.NotFoundException) {
Log.e(TAG, "Can't find style. Error: ", e)
}
}
setMapStyle()
no método onMapReady()
passando seu objeto GoogleMap
.override fun onMapReady(googleMap: GoogleMap) {
...
setMapStyle(map)
}
normal
e o novo estilo deve ser visível com temas retro e estradas da cor escolhida. Você pode personalizar seu mapa ainda mais estilizando os marcadores de mapa. Nesta etapa, você pode alterar os marcadores vermelhos padrão para algo mais moderno.
onMapLongClick()
, adicione a seguinte linha de código ao MarkerOptions()
do construtor para usar o marcador padrão, mas altere o cor para azul:.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))
onMapLongClickListener()
terá a seguinte aparência:
map.setOnMapLongClickListener { latLng ->
// A Snippet is Additional text that's displayed below the title.
val snippet = String.format(
Locale.getDefault(),
"Lat: %1$.5f, Long: %2$.5f",
latLng.latitude,
latLng.longitude
)
map.addMarker(
MarkerOptions()
.position(latLng)
.title(getString(R.string.dropped_pin))
.snippet(snippet)
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))
)
}
onPoiClick()
.Uma maneira de personalizar o mapa do Google é desenhando sobre ele. Essa técnica é útil se você deseja destacar um tipo específico de local, como pontos de pesca populares.
GroundOverlay
objects: Uma superposição de solo é uma imagem fixada em um mapa. Ao contrário dos marcadores, as superposições de solo são orientadas para a superfície da Terra e não para a tela. Girar, inclinar ou ampliar o mapa altera a orientação da imagem. As superposições de solo são úteis quando você deseja fixar uma única imagem em uma área do mapa.Nesta tarefa, você adiciona uma sobreposição de solo no formato de um Android ao local da sua casa.
res/drawable
. (Certifique-se de que o nome do arquivo seja android.png
.)onMapReady()
, após a chamada para mover a câmera para a posição de sua casa, crie um objeto GroundOverlayOptions
. androidOverlay
.val androidOverlay = GroundOverlayOptions()
BitmapDescriptorFactory.fromResource()
para criar um objeto BitmapDescriptor
a partir do recurso de imagem baixado. Passe o objeto BitmapDescriptor
resultante para o método image()
do objeto GroundOverlayOptions
.val androidOverlay = GroundOverlayOptions()
.image(BitmapDescriptorFactory.fromResource(R.drawable.android))
float overlaySize
para a largura em metros da sobreposição desejada. Para este exemplo, uma largura de 100f
funciona bem.position
para o objeto GroundOverlayOptions
chamando o método position()
. Passe o objeto homeLatLng
e o overlaySize
.val overlaySize = 100f
val androidOverlay = GroundOverlayOptions()
.image(BitmapDescriptorFactory.fromResource(R.drawable.android))
.position(homeLatLng, overlaySize)
addGroundOverlay()
no objeto GoogleMap
. Passe seu objeto GroundOverlayOptions
.map.addGroundOverlay(androidOverlay)
zoomLevel
para 18f para ver a imagem do Android como uma sobreposição.Os usuários costumam usar o Google Maps para ver sua localização atual. Para exibir a localização do dispositivo em seu mapa, você pode usar a camada de dados de localização.
A camada de dados de localização adiciona um botão My Location ao lado superior direito do mapa. Quando o usuário toca no botão, o mapa é centralizado na localização do dispositivo. A localização é mostrada como um ponto azul se o dispositivo estiver parado e como uma divisa azul se o dispositivo estiver em movimento.
Nesta tarefa, você ativa a camada de dados de localização.
Ativar o rastreamento de localização no Google Maps requer uma única linha de código. No entanto, você deve certificar-se de que o usuário concedeu permissões de localização (usando o modelo de permissão de tempo de execução).
Nesta etapa, você solicita permissões de localização e ativa o rastreamento de localização.
AndroidManifest.xml
, verifique se a permissão FINE_LOCATION
já está presente. O Android Studio inseriu essa permissão quando você selecionou o modelo do Google Maps. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
MapsActivity
, crie uma variável de classe REQUEST_LOCATION_PERMISSION
.private val REQUEST_LOCATION_PERMISSION = 1
MapsActivity
chamado isPermissionGranted()
. Neste método, verifique se o usuário concedeu a permissão.
private fun isPermissionGranted() : Boolean {
return ContextCompat.checkSelfPermission(
this,
Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
MapsActivity
chamado enableMyLocation()
que não aceita argumentos e não retorna nada. Dentro, verifique a permissão ACCESS_FINE_LOCATION
. Se a permissão for concedida, ative a camada de localização. Caso contrário, solicite a permissão.private fun enableMyLocation() {
if (isPermissionGranted()) {
map.isMyLocationEnabled = true
}
else {
ActivityCompat.requestPermissions(
this,
arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
REQUEST_LOCATION_PERMISSION
)
}
}
enableMyLocation()
do retorno de chamada onMapReady()
para habilitar a camada de localização.override fun onMapReady(googleMap: GoogleMap) {
...
enableMyLocation()
}
onRequestPermissionsResult()
. Se o requestCode
for igual a REQUEST_LOCATION_PERMISSION
, a permissão será concedida e se o array grantResults
não estiver vazio com PackageManager.PERMISSION_GRANTED
em seu primeiro slot, chame enableMyLocation()
:override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray) {
if (requestCode == REQUEST_LOCATION_PERMISSION) {
if (grantResults.contains(PackageManager.PERMISSION_GRANTED)) {
enableMyLocation()
}
}
}
O mapa agora exibirá a localização atual do dispositivo usando um ponto azul. Observe que há um botão de localização no canto superior direito. Se você mover o mapa para longe de sua localização e clicar neste botão, ele centralizará o mapa de volta na localização do dispositivo.
Baixe o código para o tutorial concluído.
$ git clone https://github.com/googletutoriais/android-kotlin-geo-maps
Alternativamente, você pode baixar o repositório como um arquivo Zip, descompactá-lo e abri-lo no Android Studio.
Activity
com um único SupportMapFragment
no layout do aplicativo. O modelo também adiciona o ACCESS_FINE_PERMISSION
ao manifesto do aplicativo e implementa o OnMapReadyCallback
em sua atividade e substitui o onMapReady()
.Para alterar o tipo de mapa de um GoogleMap
no tempo de execução, use o método GoogleMap.setMapType()
. Um mapa do Google pode ser um dos seguintes tipos de mapa:
Sobre o Google Maps:
normal
.OnPoiClickListener
.setMapStyle()
.
Outras informações importantes:
GroundOverlayOptions
para especificar a imagem, o tamanho da imagem em metros e a posição da imagem. Passe este objeto para o método GoogleMap.addGroundOverlay()
para definir a sobreposição para o mapa.ACCESS_FINE_LOCATION
, você pode ativar o rastreamento de localização definindo map.isMyLocationEnabled = true
.Documentação do desenvolvedor Android:
Documentação de referência:
Para obter links para outros tutoriais neste curso, consulte a página inicial de tutoriais do Android avançado em Kotlin.