Aspectos avanzados de Android en Kotlin 04.1: Google Maps para Android

Este codelab es parte del curso Aspectos avanzados de Android en Kotlin. Aprovecharás al máximo este curso si trabajas con los codelabs en secuencia, pero no es obligatorio. Todos los codelabs del curso se detallan en la página de destino de Codelabs avanzados de Android en Kotlin.

Si creas apps con Google Maps, puedes agregar funciones a ellas, como imágenes satelitales, controles sólidos de IU para mapas, seguimiento de ubicaciones y marcadores de ubicación. Puede agregar valor a Google Maps estándar mostrando información de su propio conjunto de datos, como las ubicaciones de áreas conocidas de pesca o alpinismo. También puedes crear juegos en los que el jugador explore el mundo físico, como una búsqueda del tesoro o incluso juegos de realidad aumentada.

En esta lección, crearás una app de Google Maps llamada Wander que muestra mapas personalizados y muestra la ubicación del usuario.

Prerequisites

Conocimientos sobre lo siguiente:

  • Cómo crear una app básica para Android y ejecutarla con Android Studio
  • Cómo crear y administrar recursos, como strings
  • Cómo refactorizar el código y cambiar el nombre de las variables con Android Studio
  • Cómo usar un mapa de Google Maps como usuario
  • Cómo configurar permisos de tiempo de ejecución

Qué aprenderás

  • Cómo obtener una clave de API en la Consola de API de Google y registrarla en tu app
  • Cómo integrar un mapa de Google en tu app
  • Cómo mostrar diferentes tipos de mapas
  • Cómo modificar el estilo del mapa de Google
  • Cómo agregar marcadores a tu mapa
  • Cómo permitir que el usuario coloque un marcador en un lugar de interés
  • Cómo habilitar el seguimiento de ubicación
  • Cómo crear la app de Wander, que tiene un mapa de Google incorporado
  • Cómo crear funciones personalizadas para tu app, como marcadores y estilos
  • Cómo habilitar el seguimiento de la ubicación en la app

En este codelab, crearás la app de Wander, que muestra un mapa de Google con un estilo personalizado. La app de Wander te permite colocar marcadores en ubicaciones, agregar superposiciones y ver tu ubicación en tiempo real.

El SDK de Maps para Android requiere una clave de API. Para obtener la clave de API, registra tu proyecto en la página de servicios y API. La clave de API está vinculada a un certificado digital que vincula la app con su autor. Para obtener más información sobre cómo usar certificados digitales y firmar tu app, consulta Cómo firmar tu app.

En este codelab, usarás la clave de API para el certificado de depuración. El certificado de depuración no es seguro por diseño, como se describe en Firma la compilación de depuración. Las apps para Android publicadas que utilizan el SDK de Maps para Android requieren una segunda clave de API: la clave para el certificado de lanzamiento. Para obtener más información sobre cómo obtener un certificado de lanzamiento, consulta Cómo obtener una clave de API.

Android Studio incluye una plantilla de Google Maps Activity, que genera código útil. El código de plantilla incluye un archivo google_maps_api.xml que contiene un vínculo que simplifica la obtención de una clave de API.

Paso 1: Crea el proyecto Wander con la plantilla de mapas

  1. Crea un nuevo proyecto de Android Studio.
  2. Selecciona la plantilla Google Maps Activity.

  1. Asigna el nombre Wander al proyecto.
  2. Establece el nivel de API mínimo en API 19. Asegúrate de que el lenguaje sea Kotlin.
  3. Haz clic en Finish.
  4. Una vez que la app termine de compilarse, consulta tu proyecto y los siguientes archivos relacionados con mapas que Android Studio creará automáticamente:

google_maps_api.xml: Usa este archivo de configuración para conservar tu clave de API. La plantilla genera dos archivos google_maps_api.xml: uno para la depuración y otro para el lanzamiento. El archivo correspondiente a la clave de API para el certificado de depuración se encuentra en src/debug/res/values. El archivo correspondiente a la clave de API para el certificado de la versión se encuentra en src/release/res/values. En este codelab, solo usarás el certificado de depuración.

activity_maps.xml: Este archivo de diseño contiene un solo fragmento que ocupa toda la pantalla. La clase SupportMapFragment es una subclase de la clase Fragment. Un elemento SupportMapFragment es la forma más sencilla de colocar un mapa en una app. Es un wrapper alrededor de la vista de un mapa para controlar automáticamente las necesidades necesarias del ciclo de vida.

Puedes incluir SupportMapFragment en un archivo de diseño mediante una etiqueta <fragment> en cualquier ViewGroup, con un atributo name adicional.

android:name="com.google.android.gms.maps.SupportMapFragment"

MapsActivity.java: El archivo MapsActivity.kt crea una instancia de SupportMapFragment en el método onCreate() y usa la clase getMapAsync() para inicializar automáticamente el sistema de mapas y la vista. La actividad que contiene SupportMapFragment debe implementar la interfaz OnMapReadyCallback y el método onMapReady() de esa interfaz. Se llama al método onMapReady() cuando se carga el mapa.

Paso 2: Obtén la clave de API

  1. Abre la versión de depuración del archivo google_maps_api.xml.
  2. En el archivo, busca un comentario con una URL larga. Los parámetros de URL incluyen información específica sobre tu app.
  3. Copie y pegue la URL en un navegador.
  4. Sigue las indicaciones para crear un proyecto en la página de servicios y API. Debido a los parámetros de la URL proporcionada, la página sabe habilitar automáticamente el SDK de Maps para Android.
  5. Haz clic en Crear una clave de API.
  6. En la página siguiente, ve a la sección Claves de API y haz clic en la clave que acabas de crear.
  7. Haz clic en Restringir clave y selecciona SDK de Maps para Android a fin de restringir el uso de la clave a las apps para Android.
  8. Copie la clave de API generada. Comienza con AIza".
  9. En el archivo google_maps_api.xml, pega la clave en la string google_maps_key, donde dice YOUR_KEY_HERE.
  10. Ejecuta la app. Deberías ver un mapa incorporado en tu actividad con un marcador establecido en Sídney, Australia. (El marcador de Sídney es parte de la plantilla y lo cambiarás más adelante).

Paso 3: Cambia el nombre de mMap

MapsActivity tiene un lateinit var privado llamado mMap, que es del tipo GoogleMap. Para seguir las convenciones de nombres de Kotlin, cambia el nombre de mMap a map.

  1. En MapsActivity, haz clic con el botón derecho en mMap y luego con el botón derecho en Refactor > Cambiar nombre...

  1. Cambia el nombre de la variable a map.

Observa cómo todas las referencias a mMap en la función onMapReady() también cambian a map.

Google Maps incluye varios tipos de mapas: normal, híbrido, satelital, de terreno y "ninguno" (para ningún mapa).

Mapa normal

Mapa satelital

Mapa híbrido

Mapa de terreno

Cada tipo de mapa proporciona diferentes tipos de información. Por ejemplo, al utilizar mapas para la navegación en un automóvil, es útil ver los nombres de las calles para poder utilizar la opción normal. Cuando haces senderismo, el mapa de terreno podría ser útil para decidir cuánto más tienes que escalar para llegar a la cima.

En esta tarea, hará lo siguiente:

  1. Agrega una barra de la aplicación con un menú de opciones que permita al usuario cambiar el tipo de mapa.
  2. Mueve la ubicación inicial del mapa a tu propia dirección particular.
  3. Se agregó compatibilidad con marcadores, que indican ubicaciones únicas en un mapa y pueden incluir una etiqueta.

Cómo agregar un menú para tipos de mapas

En este paso, agregarás una barra de la aplicación con un menú de opciones que permite al usuario cambiar el tipo de mapa.

  1. Para crear un nuevo archivo en menú XML, haz clic con el botón derecho en el directorio res y selecciona New > Android Resource File.
  2. En el cuadro de diálogo, asigna un nombre al archivo map_options.
  3. Elige Menú para el tipo de recurso.
  4. Haga clic en OK.
  5. En la pestaña Code, reemplaza el código del archivo nuevo por el siguiente código para crear las opciones del menú del mapa. Se omite el tipo de mapa porque no se incluye ningún mapa. Este paso causa un error, pero lo resolverás en el paso siguiente.
<?xml version="1.0" encoding="utf-8"?>
<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>
  1. En strings.xml, agrega recursos para los atributos title a fin de resolver los errores.
<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>
  1. En MapsActivity, anula el método onCreateOptionsMenu() y aumenta el menú del archivo de recursos map_options.
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
   val inflater = menuInflater
   inflater.inflate(R.menu.map_options, menu)
   return true
}
  1. En MapsActivity.kt, anula el método onOptionsItemSelected(). Cambia el tipo de mapa utilizando constantes de tipo de mapa para reflejar la selección del usuario.
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)
}
  1. Ejecuta la app.
  2. Haz clic en para cambiar el tipo de mapa. Observa cómo cambia la apariencia del mapa entre los diferentes modos.

De forma predeterminada, la devolución de llamada onMapReady() incluye código que coloca un marcador en Sídney, Australia, donde se creó Google Maps. En la devolución de llamada predeterminada, también se anima el mapa para desplazarse lateralmente en Sídney.

En esta tarea, harás que la cámara de tu mapa se desplace a tu casa, acerques el mapa al nivel que especifiques y coloques un marcador allí.

Paso 1: Haz zoom en tu casa y agrega un marcador

  1. En el archivo MapsActivity.kt, busca el método onMapReady(). Quita el código que se encuentra en esa ubicación y mueve la cámara. Así debería verse tu método ahora.
override fun onMapReady(googleMap: GoogleMap) {
   map = googleMap

}
  1. Sigue estas instrucciones para encontrar la latitud y longitud de tu casa.
  2. Crea un valor para la latitud y un valor para la longitud, y luego ingresa sus valores flotantes.
val latitude = 37.422160
val longitude = -122.084270
  1. Crea un nuevo objeto LatLng llamado homeLatLng. En el objeto homeLatLng, pasa los valores que acabas de crear.
val homeLatLng = LatLng(latitude, longitude)
  1. Crea un objeto val de qué tan acercado quieres que esté el mapa. Usa el nivel de zoom 15f.
val zoomLevel = 15f

El nivel de zoom controla el nivel de zoom del mapa. La siguiente lista te da una idea del nivel de detalle que muestra cada nivel de zoom:

  • 1: Mundo
  • 5: Tierra firme y continente
  • 10: Ciudad
  • 15: Calles
  • 20: Edificios
  1. Mueve la cámara a homeLatLng llamando a la función moveCamera() en el objeto map y pasa un objeto CameraUpdate con CameraUpdateFactory.newLatLngZoom(). Pasa el objeto homeLatLng y el zoomLevel.
map.moveCamera(CameraUpdateFactory.newLatLngZoom(homeLatLng, zoomLevel))
  1. Agrega un marcador al mapa en homeLatLng.
map.addMarker(MarkerOptions().position(homeLatLng))

El método final debería verse de la siguiente manera:

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))
}
  1. Ejecuta tu app. El mapa debería desplazarse lateralmente hasta tu casa, acercar el nivel deseado y colocar un marcador en la casa.

Paso 2: Permite que los usuarios agreguen un marcador con un clic largo

En este paso, agregarás un marcador cuando el usuario mantenga y mantenga una ubicación en el mapa.

  1. Crea un stub de método en MapsActivity llamado setMapLongClick() que tome un elemento GoogleMap como argumento.
  2. Adjunta un objeto de escucha setOnMapLongClickListener al objeto de mapa.
private fun setMapLongClick(map:GoogleMap) {
   map.setOnMapLongClickListener { }
}
  1. En setOnMapLongClickListener(), llama al método addMarker(). Pasa un nuevo objeto MarkerOptions con la posición establecida en el LatLng que se pasó.
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. Al final del método onMapReady(), llama a setMapLongClick() con map.
override fun onMapReady(googleMap: GoogleMap) {
   ...
  
   setMapLongClick(map)
}
  1. Ejecuta tu app.
  2. Mantén presionado el mapa para colocar un marcador en una ubicación.
  3. Presiona el marcador que lo centra en la pantalla.

Paso 3: Agrega una ventana de información para el marcador

En este paso, agregas un elemento InfoWindow que muestra las coordenadas del marcador cuando se lo presiona.

  1. En setMapLongClick()setOnMapLongClickListener(), crea un val para snippet. Un fragmento es texto adicional que se muestra después del título. El fragmento muestra la latitud y la longitud de un marcador.
private fun setMapLongClick(map: GoogleMap) {
   map.setOnMapLongClickListener { latLng ->
       // A snippet is additional text that's displayed after the title.
       val snippet = String.format(
           Locale.getDefault(),
           "Lat: %1$.5f, Long: %2$.5f",
           latLng.latitude,
           latLng.longitude
       )
       map.addMarker(
           MarkerOptions()
               .position(latLng)
       )
   }
}
  1. En addMarker(), configura el elemento title del marcador en Pin colocado con un recurso de strings R.string.dropped_pin.
  2. Establece el marcador snippet del marcador en snippet.

La función completada se ve de la siguiente manera:

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)
              
       )
   }
}
  1. Ejecuta tu app.
  2. Mantén presionado el mapa para colocar un marcador de ubicación.
  3. Presiona el marcador para que aparezca la ventana de información.

Paso 4: Agrega un objeto de escucha de lugares de interés

De forma predeterminada, los lugares de interés aparecen en el mapa junto con sus íconos correspondientes. Los lugares de interés incluyen parques, escuelas, edificios gubernamentales y mucho más. Cuando se configura el tipo de mapa como normal, los lugares de interés comerciales también aparecen en el mapa. Estos representan empresas, como tiendas, restaurantes y hoteles.

En este paso, agregarás una GoogleMap.OnPoiClickListener al mapa. Este objeto de escucha de clics coloca un marcador en el mapa de inmediato cuando el usuario hace clic en un lugar de interés. El objeto de escucha de clics también muestra una ventana de información que contiene el nombre del lugar de interés.

  1. Crea un stub de método en MapsActivity llamado setPoiClick() que tome un elemento GoogleMap como argumento.
  2. En el método setPoiClick(), establece un OnPoiClickListener en el GoogleMap pasado.
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->

   }
}
  1. En setOnPoiClickListener(), crea un val poiMarker para el marcador .
  2. Configúralo en un marcador con map.addMarker() y MarkerOptions configurando title en el nombre del lugar de interés.
private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
   }
}
  1. En la función setOnPoiClickListener(), llama a showInfoWindow() en poiMarker para mostrar inmediatamente la ventana de información.
poiMarker.showInfoWindow()

El código final para la función setPoiClick() debería verse así:

private fun setPoiClick(map: GoogleMap) {
   map.setOnPoiClickListener { poi ->
       val poiMarker = map.addMarker(
           MarkerOptions()
               .position(poi.latLng)
               .title(poi.name)
       )
       poiMarker.showInfoWindow()
   }
}
  1. Al final de onMapReady(), llama a setPoiClick() y pasa map.
override fun onMapReady(googleMap: GoogleMap) {
   ...

   setPoiClick(map)
}
  1. Ejecuta tu app y busca un lugar de interés, como un parque o una cafetería.
  2. Presiona el lugar de interés para colocar un marcador y muestra el nombre del lugar de interés en una ventana de información.

Puedes personalizar Google Maps de muchas formas, con lo que tu mapa tendrá un aspecto único.

Puedes personalizar un objeto MapFragment con los atributos XML disponibles, como lo harías con cualquier otro fragmento. Sin embargo, en este paso, personalizarás el aspecto del contenido de MapFragment mediante métodos en el objeto GoogleMap.

Para crear un estilo personalizado para tu mapa, generas un archivo JSON que especifica cómo se muestran los elementos en el mapa. No tienes que crear este archivo JSON manualmente. Google proporciona el Asistente de diseño de Maps Platform, que genera el archivo JSON por ti después de diseñar tu mapa visualmente. En esta tarea, modificarás el estilo del mapa con un tema retro, lo que significa que el mapa tendrá colores vintage y agregarás caminos de colores.

Paso 1: Crea un estilo para el mapa

  1. Ve a https://mapstyle.withgoogle.com/ en tu navegador.
  2. Selecciona Crear un estilo.
  3. Selecciona Retro.

  1. Haz clic en Más opciones.

  1. En la lista Feature type, selecciona Road > Fill.
  2. Cambia el color de las rutas por cualquier color que elijas (como rosa).

  1. Haz clic en Finish.

  1. Copia el código JSON del diálogo resultante y, si lo deseas, guárdalo en una nota de texto sin formato para usarlo en el siguiente paso.

Paso 2: Agrega el estilo a tu mapa

  1. En Android Studio, en el directorio res , crea un directorio de recursos y asígnale el nombre raw. Puedes usar los recursos del directorio raw, como el código JSON.
  2. Crea un archivo en res/raw llamado map_style.json.
  3. Pega el código JSON guardado en el archivo de recursos nuevo.
  4. En MapsActivity, crea una variable de clase TAG sobre el método onCreate(). Se usa con fines de registro.
private val TAG = MapsActivity::class.java.simpleName
  1. También en MapsActivity, crea una función setMapStyle() que tome un GoogleMap.
  2. En setMapStyle(), agrega un bloque try{}.
  3. En el bloque try{}, crea un val success para el éxito del diseño. (agregas el siguiente bloque de captura).
  4. En el bloque try{}, configura el estilo JSON en el mapa y llama a setMapStyle() en el objeto GoogleMap. Pasa un objeto MapStyleOptions, que carga el archivo JSON.
  5. Asigna el resultado a success. El método setMapStyle() muestra un valor booleano que indica el estado de éxito del análisis del archivo de estilo y la configuración del 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
           )
       )
   }
}
  1. Se agregó una sentencia if para que success sea falsa. Si el estilo no se realiza correctamente, imprime un registro en el que haya fallado el análisis.
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
       if (!success) {
           Log.e(TAG, "Style parsing failed.")
       }
   }
}
  1. Agrega un bloque catch{} para controlar la situación de un archivo de estilo faltante. En el bloque catch, si no se puede cargar el archivo, muestra una Resources.NotFoundException.
private fun setMapStyle(map: GoogleMap) {
   try {
       ...
   } catch (e: Resources.NotFoundException) {
       Log.e(TAG, "Can't find style. Error: ", e)
   }
}

El método finalizado debería verse como el siguiente fragmento de código:

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)
   }
}
  1. Por último, llama al método setMapStyle() en el método onMapReady() pasando tu objeto GoogleMap.
override fun onMapReady(googleMap: GoogleMap) {
   ...
   setMapStyle(map)
}
  1. Ejecuta tu app.
  2. Establece el mapa en el modo normal para que el nuevo estilo sea visible con temas retro y rutas del color que elijas.

Paso 3: Diseña tu marcador

Puedes personalizar aún más tu mapa si le aplicas estilo a los marcadores del mapa. En este paso, cambiarás los marcadores rojos predeterminados por uno más espectacular.

  1. En el método onMapLongClick(), agrega la siguiente línea de código al MarkerOptions() del constructor para usar el marcador predeterminado, pero cambia el color a azul.
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_BLUE))

Ahora, onMapLongClickListener() se ve así:

map.setOnMapLongClickListener { latLng ->
   // A snippet is additional text that's displayed after 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))
   )
}
  1. Ejecuta la app. Los marcadores que aparecen después de un clic largo ahora aparecen sombreados en azul. Ten en cuenta que los marcadores de lugares de interés siguen siendo rojos porque no agregaste ningún estilo al método onPoiClick().

Una forma de personalizar el mapa de Google es dibujando arriba. Esta técnica es útil si quieres destacar un tipo de ubicación en particular, como lugares populares de pesca.

  • Formas: Puedes agregar polilíneas, polígonos y círculos al mapa.
  • Objetos GroundOverlay: Una superposición de suelo es una imagen que se fija a un mapa. A diferencia de los marcadores, las superposiciones de suelo están orientadas a la superficie de la Tierra y no a la pantalla. Al rotar, inclinar o acercar el mapa cambia la orientación de la imagen. Las superposiciones de suelo resultan útiles cuando deseas fijar una sola imagen en un área del mapa.

Paso: Agrega una superposición de suelo

En esta tarea, agregarás una superposición de suelo en forma de Android a la ubicación de tu casa.

  1. Descarga esta imagen de Android y guárdala en tu carpeta res/drawable. (Asegúrate de que el nombre del archivo sea android.png).

  1. En onMapReady(), después de la llamada para mover la cámara a la posición de tu casa, crea un objeto GroundOverlayOptions.
  2. Asigna el objeto a una variable llamada androidOverlay.
val androidOverlay = GroundOverlayOptions()
  1. Usa el método BitmapDescriptorFactory.fromResource() para crear un objeto BitmapDescriptor a partir del recurso de imagen descargado.
  2. Pasa el objeto BitmapDescriptor resultante al método image() del objeto GroundOverlayOptions.
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
  1. Crea un objeto float overlaySize para el ancho en metros de la superposición deseada. En este ejemplo, un ancho de 100f funciona bien.

A fin de establecer la propiedad position para el objeto GroundOverlayOptions, llama al método position() y pasa el objeto homeLatLng y el overlaySize.

val overlaySize = 100f
val androidOverlay = GroundOverlayOptions()
   .image(BitmapDescriptorFactory.fromResource(R.drawable.android))
   .position(homeLatLng, overlaySize)
  1. Llama a addGroundOverlay() en el objeto GoogleMap y pasa el objeto GroundOverlayOptions.
map.addGroundOverlay(androidOverlay)
  1. Ejecuta la app.
  2. Cambia el valor de zoomLevel a 18f para ver la imagen de Android como una superposición.

Los usuarios suelen usar Google Maps para ver su ubicación actual. Para mostrar la ubicación del dispositivo en el mapa, puedes usar la capa de datos de ubicación.

La capa de datos de ubicación agrega Mi ubicación al mapa Cuando el usuario presiona el botón, el mapa se centra en la ubicación del dispositivo. La ubicación se muestra como un punto azul si el dispositivo está quieto y como una comilla angular azul si está en movimiento.

En esta tarea, habilitará la capa de datos de ubicación.

Paso: Solicita permisos de ubicación

Para habilitar el seguimiento de ubicación en Google Maps, se requiere una sola línea de código. Sin embargo, debes asegurarte de que el usuario haya otorgado permisos de ubicación (con el modelo de permisos de tiempo de ejecución).

En este paso, solicitas los permisos de ubicación y habilitas el seguimiento de ubicación.

  1. En el archivo AndroidManifest.xml, verifica que el permiso FINE_LOCATION ya esté presente. Android Studio insertó este permiso cuando seleccionaste la plantilla de Google Maps.
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  1. En MapsActivity, crea una variable de clase REQUEST_LOCATION_PERMISSION.
private val REQUEST_LOCATION_PERMISSION = 1
  1. Para verificar si se otorgaron los permisos, crea un método en la MapsActivity que se llame isPermissionGranted(). En este método, verifica si el usuario otorgó el permiso.
private fun isPermissionGranted() : Boolean {
  return ContextCompat.checkSelfPermission(
       this,
      Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED
}
  1. Para habilitar el seguimiento de la ubicación en tu app, crea un método en MapsActivity llamado enableMyLocation() que no acepte argumentos y que no muestre nada. Adentro, busca el permiso ACCESS_FINE_LOCATION. Si se otorgó el permiso, habilita la capa de ubicación. De lo contrario, solicita el permiso.
private fun enableMyLocation() {
   if (isPermissionGranted()) {
       map.isMyLocationEnabled = true 
   }
   else {
       ActivityCompat.requestPermissions(
           this,
           arrayOf<String>(Manifest.permission.ACCESS_FINE_LOCATION),
           REQUEST_LOCATION_PERMISSION
       )
   }
}
  1. Llama a enableMyLocation() desde la devolución de llamada onMapReady() para habilitar la capa de ubicación.
override fun onMapReady(googleMap: GoogleMap) {
   ...
   enableMyLocation()
}
  1. Reemplaza el método onRequestPermissionsResult(). Si el permiso requestCode es igual al permiso REQUEST_LOCATION_PERMISSION y si el array grantResults no está vacío con PackageManager.PERMISSION_GRANTED en su primer espacio publicitario, llama a enableMyLocation().
override fun onRequestPermissionsResult(
   requestCode: Int,
   permissions: Array<String>,
   grantResults: IntArray) {
   if (requestCode == REQUEST_LOCATION_PERMISSION) {
       if (grantResults.contains(PackageManager.PERMISSION_GRANTED)) {
           enableMyLocation()
       }
   }
}
  1. Ejecuta la app. Debería aparecer un diálogo que solicite acceso a la ubicación del dispositivo. Otorga permiso.

Ahora, el mapa muestra la ubicación actual del dispositivo usando un punto azul. Observa que hay un botón de ubicación. Si alejas el mapa de tu ubicación y haces clic en este botón, el mapa vuelve a centrarlo en la ubicación del dispositivo.

Descarga el código del codelab terminado.

$  git clone https://github.com/googlecodelabs/android-kotlin-geo-maps


También puedes descargar el repositorio como un archivo ZIP, descomprimirlo y abrirlo en Android Studio.

Descargar ZIP

  • Para usar la API de Google Maps, necesitas una clave de API de la Consola de API de Google.
  • En Android Studio, el uso de la plantilla Actividad de Google Maps genera una Activity con un solo SupportMapFragment en el diseño de la app. La plantilla también agrega ACCESS_FINE_PERMISSION al manifiesto de la app, implementa el OnMapReadyCallback en tu actividad y anula el método onMapReady() requerido.

Para cambiar el tipo de mapa de un objeto GoogleMap en el tiempo de ejecución, usa el método GoogleMap.setMapType(). Los mapas pueden ser de uno de los siguientes tipos:

  • Normal: Mapa de ruta típico. Muestra rutas, algunos elementos creados por el hombre y elementos naturales importantes, como ríos. También muestra etiquetas de rutas y elementos.
  • Híbrido: Datos de fotos satelitales con mapas de rutas agregados También muestra etiquetas de rutas y elementos.
  • Satélite: Muestra datos de la fotografía. No muestra etiquetas de rutas y características.
  • Terreno: Datos topográficos. El mapa incluye colores, líneas de contornos y etiquetas, y sombras que agregan perspectiva. También muestra algunas rutas y etiquetas.
  • Ninguno: No hay mosaicos de mapa base.

Acerca de Google Maps:

  • Un marcador es un indicador de una ubicación geográfica específica.
  • Cuando se lo presiona, el comportamiento predeterminado del marcador es mostrar una ventana de información con información sobre la ubicación.
  • De forma predeterminada, los puntos de interés (POI) aparecen en el mapa básico con sus íconos correspondientes. Los lugares de interés incluyen parques, escuelas, edificios gubernamentales y mucho más.
  • Además, los lugares de interés comerciales (tiendas, restaurantes, hoteles, etc.) aparecen de forma predeterminada en el mapa cuando el tipo de mapa es normal.
  • Puedes captar los clics en los lugares de interés con OnPoiClickListener.
  • Puedes cambiar el aspecto visual de casi todos los elementos de un mapa de Google mediante el Asistente de diseño. El asistente de diseño genera un archivo JSON que se pasa al mapa de Google con el método setMapStyle().
  • Para personalizar tus marcadores, cambia el color predeterminado o reemplaza el ícono de marcador predeterminado por una imagen personalizada.

Otra información importante:

  • Usa una superposición de suelo para corregir la imagen en una ubicación geográfica.
  • Usa un objeto GroundOverlayOptions para especificar la imagen, su tamaño en metros y la posición de la imagen. Pasa este objeto al método GoogleMap.addGroundOverlay() para establecer la superposición con el mapa.
  • Siempre y cuando la app tenga el permiso ACCESS_FINE_LOCATION, podrás habilitar el seguimiento de la ubicación configurando map.isMyLocationEnabled = true.
  • No se incluye en este codelab, pero puedes proporcionar información adicional sobre una ubicación con Google Street View, que es una foto panorámica navegable de una ubicación determinada.

Documentación para desarrolladores de Android:

Documentación de referencia:

Para obtener vínculos a otros codelabs de este curso, consulta la página de destino de Codelabs avanzados de Android en Kotlin.