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

1. Antes de comenzar

Crear apps con Google Maps te permite agregar funciones a tu app, como imágenes satelitales, controles de IU sólidos para mapas, seguimiento de la ubicación y marcadores de ubicación. Puedes agregar valor a Google Maps estándar mostrando información de tu propio conjunto de datos, como las ubicaciones de zonas de pesca o escalada conocidas. 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 la ubicación del usuario.

Requisitos previos

Conocimientos de lo siguiente:

  • Comprender cómo crear una app básica para Android y ejecutarla con Android Studio
  • Cómo crear y administrar recursos, como cadenas de texto
  • Cómo refactorizar el código y cambiar el nombre de 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 desde la Consola de APIs de Google y registrarla en tu app
  • Cómo integrar un mapa de Google Maps en tu app
  • Cómo mostrar diferentes tipos de mapas
  • Cómo aplicar diseño al mapa de Google Maps
  • 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 ubicación en tu app

2. Descripción general de la app

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

5b12eda7f467bc2f.png

3. Tarea: Configura el proyecto y obtén una clave de API

Maps SDK for Android requiere una clave de API. Para obtener la clave de API, registra tu proyecto en el archivo API & Página Servicios La clave de API está vinculada a un certificado digital que vincula la app con su autor. Para obtener más información sobre el uso de certificados digitales y la firma de apps, consulta Firma tu app.

En este codelab, usarás la clave de API para el certificado de depuración. El certificado de depuración tiene un diseño inseguro, como se describe en Cómo firmar tu compilación de depuración. Las aplicaciones para Android publicadas que utilizan el SDK de Maps para Android requieren una segunda clave de API: la clave del certificado de lanzamiento. Para conocer más detalles sobre cómo obtener un certificado de lanzamiento, consulta Cómo obtener una clave de API.

Android Studio incluye una plantilla de actividad de Google Maps, que genera un código de plantilla útil. El código de la 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.

d6b874bb19ea68cd.png

  1. Asígnale 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 Finalizar.
  4. Una vez que se termine de compilar la app, observa tu proyecto y los siguientes archivos relacionados con mapas que Android Studio crea por ti:

google_maps_api.xml: usas este archivo de configuración para contener 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 de la clave de API para el certificado de depuración se encuentra en src/debug/res/values. El archivo de la clave de API para el certificado de lanzamiento 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. Una SupportMapFragment es la manera más sencilla de colocar un mapa en una app. Se trata de un wrapper alrededor de una vista de un mapa que controla automáticamente las necesidades necesarias del ciclo de vida.

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

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

MapsActivity.java: El archivo MapsActivity.java crea una instancia de SupportMapFragment en el método onCreate() y usa el archivo getMapAsync() para inicializar automáticamente el sistema de mapas y la vista. La actividad que contiene el SupportMapFragment debe implementar la interfaz de 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 la URL incluyen información específica sobre tu app.
  3. Copia y pega la URL en un navegador.
  4. Sigue las indicaciones para crear un proyecto en la página APIs y Página Servicios Debido a los parámetros de la URL proporcionada, la página sabe que debe 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 para restringir el uso de la clave a las apps para Android.
  8. Copia la clave de API generada. Comienza con "AIza".
  9. En el archivo google_maps_api.xml, pega la clave en la cadena google_maps_key, donde dice YOUR_KEY_HERE.
  10. Ejecuta tu app. Deberías ver un mapa incorporado en tu actividad con un marcador establecido en Sídney, Australia. (El marcador Sídney es parte de la plantilla y la podrás cambiar más adelante).

34dc9dd877c90996.png

Paso 3: Cambia el nombre de mMap

MapsActivity tiene un var privado de lateinit 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, en Refactor > Cambiar nombre...

e713ccb3384450c6.png

  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.

4. Tarea: Agrega tipos de mapas

Google Maps incluye varios tipos de mapas: normales, híbridos, satelitales, de terreno y "ninguno" (ya que no hay ningún mapa).

Mapa normal

Mapa de satélite

Mapa híbrido

Mapa de terreno

Cada tipo de mapa brinda diferentes clases de información. Por ejemplo, al usar mapas para navegar en un automóvil, es útil ver los nombres de las calles, por lo que podrías usar la opción normal. Cuando caminas por una caminata, el mapa de terreno puede ser útil para decidir cuánto más debes escalar para llegar a la cima.

En esta tarea, harás lo siguiente:

  1. Agrega una barra de la app con un menú de opciones que permite al usuario cambiar el tipo de mapa.
  2. Mueve la ubicación de partida del mapa a la de tu casa.
  3. Agrega compatibilidad con marcadores, que indican ubicaciones únicas en un mapa y pueden incluir una etiqueta.

Agregar menú para los tipos de mapas

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

  1. Para crear un nuevo archivo en formato XML de menú, haz clic con el botón derecho en el directorio res y selecciona New > Archivo de recursos de Android.
  2. En el diálogo, asigna el nombre map_options al archivo.
  3. Elige Menú para el tipo de recurso.
  4. Haz clic en Aceptar.
  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. El argumento "ninguno" el tipo de mapa se omite porque "ninguno" resulta en la falta de ningún mapa. Este paso genera 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 para 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ú desde el 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 con las 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 428da163b831115b.png para cambiar el tipo de mapa. Observa cómo cambia la apariencia del mapa entre los diferentes modos.

6fa42970d87f5dc7.png

5. Tarea: Agrega marcadores

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

En esta tarea, harás que la cámara del mapa se mueva a tu casa, harás zoom en un nivel que especifiques y colocarás un marcador allí.

Paso 1: Acerca la imagen de tu casa y agrega un marcador

  1. En el archivo MapsActivity.kt, busca el método onMapReady(). Quita el código que coloca el marcador en Sídney 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 objeto LatLng nuevo llamado homeLatLng. En el objeto homeLatLng, pasa los valores que acabas de crear.
val homeLatLng = LatLng(latitude, longitude)
  1. Crea un objeto val para determinar el nivel de zoom que deseas mostrar en el mapa. Usa el nivel de zoom 15f.
val zoomLevel = 15f

El nivel de zoom controla cuánto te acercas en el mapa. La siguiente lista te brinda 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 hacia tu casa, acercar el zoom al nivel que desees y colocar un marcador en ella.

fc939024778ee76.png

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

En este paso, se agrega un marcador cuando el usuario toca y mantiene una ubicación en el mapa.

  1. Crea un stub de método en MapsActivity llamado setMapLongClick() que tome un 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 objeto MarkerOptions nuevo con la posición establecida en el LatLng pasado.
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.

4ff8d1c1db3bca9e.png

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

En este paso, agregarás una InfoWindow que muestre 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. Tu fragmento muestra la latitud y 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(), establece el title del marcador en Pin colocado con un recurso de cadenas R.string.dropped_pin.
  2. Establece el 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. Para colocar un marcador de ubicación, mantén presionado el mapa.
  3. Presiona el marcador para que aparezca la ventana de información.

63f210e6e47dfa29.png

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 el tipo de mapa se establece en normal, también aparecen en el mapa los lugares de interés comerciales. Estos representan a empresas, como tiendas, restaurantes y hoteles.

En este paso, agregarás un objeto 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 GoogleMap como argumento.
  2. En el método setPoiClick(), configura 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 usando map.addMarker() con MarkerOptions y establece 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 de inmediato la ventana de información.
poiMarker.showInfoWindow()

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

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 en él y mostrar su nombre en una ventana de información.

f4b0972c75d5fa5f.png

6. Tarea: Aplica diseño a tu mapa

Puedes personalizar Google Maps de muchas formas para darle a tu mapa 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 la apariencia del contenido de MapFragment mediante métodos en el objeto GoogleMap.

Para crear un diseño personalizado para tu mapa, debes generar un archivo JSON que especifique cómo se muestran los componentes del mapa. No tienes que crear este archivo JSON manualmente. Google proporciona el Asistente de diseño de Maps Platform, que genera el archivo JSON para ti después de que aplicas diseño visual al mapa. En esta tarea, aplicarás diseño al mapa con un tema retro, lo que significa que, en el mapa, se usan colores vintage y se agregan rutas con colores.

Paso 1: Crea un diseño para tu mapa

  1. Navega a https://mapstyle.withgoogle.com/ en tu navegador.
  2. Selecciona Crear un diseño.
  3. Selecciona Retro.

208b3d3aeab0d9b6.png

  1. Haz clic en Más opciones.

4a35faaf9535ee82.png

  1. Selecciona Ruta > Completar.
  2. Cambia el color de las rutas al color que elijas (como rosa).

92c3293749293a4c.png

  1. Haz clic en Finalizar.

f1bfe8585eb69480.png

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

3c32168b299d6420.png

Paso 2: Agrega el diseño a tu mapa

  1. En Android Studio, en el directorio res, crea un directorio de recursos y asígnale el nombre raw. Usas 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 nuevo archivo de recursos.
  4. En MapsActivity, crea una variable de clase TAG sobre el método onCreate(). Esto se usa para 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 que los estilos se ejecuten de forma correcta. (Agregas el siguiente bloque catch).
  4. En el bloque try{}, establece el diseño 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 correcto al analizar el archivo de diseño y configurar el 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. Agrega una sentencia if para que success sea falsa. De lo contrario, imprime un registro en el que se haya producido un error en 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, muestra una Resources.NotFoundException si no se puede cargar el archivo.
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() y pasa tu objeto GoogleMap.
override fun onMapReady(googleMap: GoogleMap) {
   ...
   setMapStyle(map)
}
  1. Ejecuta tu app.
  2. Configura el mapa en el modo normal, y el nuevo diseño debería poder verse con los temas retro y las rutas del color que elijas.

b59d6cb81f02a14f.png

Paso 3: Aplica diseño al marcador

Puedes aplicar diseño a los marcadores del mapa para personalizar aún más tu mapa. En este paso, cambiarás los marcadores rojos predeterminados por algo más genial.

  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 de la siguiente manera:

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 hacer clic largo ahora aparecen sombreados en color azul. Ten en cuenta que los marcadores de lugares de interés continúan en rojo porque no agregaste un diseño al método onPoiClick().

b9916bca3c367e3.png

7. Tarea: Agrega una superposición

Una forma de personalizar el mapa de Google Maps es dibujar sobre él. Esta técnica es útil si quieres destacar un tipo de ubicación en particular, como puntos de pesca populares.

  • Formas: Puedes agregar polilíneas, polígonos y círculos al mapa.
  • GroundOverlay Objetos: Una superposición de suelo es una imagen que se fija a un mapa. A diferencia de los marcadores, las superposiciones de suelo se orientan a la superficie de la Tierra y no a la pantalla. La rotación, la inclinación o el zoom del mapa cambian 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 con 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.

61fabd56a0841b44.png

  1. En onMapReady(), después de la llamada para mover la cámara a la posición de la 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 float overlaySize para el ancho en metros de la superposición deseada. Para este ejemplo, un ancho de 100f funciona bien.

Configura la propiedad position para el objeto GroundOverlayOptions llamando 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 tu 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.

b1b25b0acd6a9807.png

8. Tarea: Habilita el seguimiento de la ubicación

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

La capa de datos de ubicación agrega el ícono Mi ubicación al mapa.

f317f84dcb3ac3a1.png

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 un corchete angular azul si el dispositivo está en movimiento.

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

Paso: Solicita permisos de ubicación

Para habilitar el seguimiento de ubicación en Google Maps, se necesita 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, debes solicitar permisos de ubicación y habilitar 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 MapsActivity llamado isPermissionGranted(). En este método, se 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 ubicación en tu app, crea un método en MapsActivity llamado enableMyLocation() que no tome argumentos ni muestre nada. Adentro, verifica el permiso ACCESS_FINE_LOCATION. Si se otorga 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. Anula el método onRequestPermissionsResult(). Verifica si requestCode es igual a REQUEST_LOCATION_PERMISSION. Si es así, significa que se otorga el permiso. Si se otorga el permiso, también verifica si el array grantResults contiene PackageManager.PERMISSION_GRANTED en su primera ranura. Si es así, 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 tu app. Debería aparecer un diálogo que solicite acceso a la ubicación del dispositivo. Continúa y otorga permiso.

da7e23e00ec762c1.png

En el mapa, se mostrará la ubicación actual del dispositivo con 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 se centra nuevamente en la ubicación del dispositivo.

5b12eda7f467bc2f.png

9. Código de solución

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

10. Resumen

¡Felicitaciones! Agregaste un mapa de Google Maps a una app de Kotlin para Android y le cambiaste el diseño.

11. Más información

Documentación para desarrolladores de Android:

Documentación de referencia:

12. Próximo codelab

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