Si andas buscando como usar la api de Google Maps v2 en Android, este tutorial es para ti.
Verás la forma de implementar las características de esta tecnología para crear apps similares a Easy Taxi, Uber, Trivago, etc.
Usar mapas en aplicaciones Android permite aumentar la utilidad de un servicio o característica que proporcione valor agregado y facilidades al usuario que requiera asociar ubicaciones a sus necesidades.
Si sigues leyendo, aprenderás a:
- Descargar y configurar los Google Play Services
- Obtener huella SHA-1
- Crear Nuevo Proyecto En Google Console Developers
- Instalar Google Play Services En Emulador
- Agregar api key a la app
- Añadir un mapa a tu aplicación
- Template MapActivity en Android Studio
- Gestos
- Controles UI
- Markers
- Tipos de mapas
- Mover la cámara
- Formas
- Manejo de eventos
Descargar Código De Google Maps En Android
Para seguir las lecciones, usaré una app de ejemplo llamada «Google Maps En Android». Puedes desbloquear el link de descarga convirtiéndote en suscriptor de Hermosa Programación:
[sociallocker id=»7121″]
Así que crea un nuevo proyecto en Android Studio con ese nombre y ve probando las enseñanzas del artículo.
1. Descargar Y Configurar Google Play Services
Google Play Services es un conjunto de librerías de Google que brindan a los desarrolladores las funcionalidades de las aplicaciones de Google como Gmail, Analytics, Google Fit, etc. Al igual que Google Maps.
Para agregar la dependencia en nuestro proyecto primero debes instalar el complemento disponible en el SDK.
Dentro de Android Studio dirígete a Tools > Android > SDK Manager:
En las opciones que verás, selecciona la pestaña SDK Tools, localiza el paquete Google Play Services y marca su respectivo checkbox.
Al presionar OK debes confirmar el inicio de la instalación:
El siguiente paso es aceptar la licencia:
Espera a que termine la descarga y pegado de los archivos:
Añadir dependencia a build.gradle
1. Ve al menú File > Project Structure…
2. Selecciona el módulo app, sitúate en la pestaña Dependencies y agrega una nueva Library Dependency.
3. Busca la librería indicando el texto descriptivo «com.google.android.gms» y selecciona aquella que tenga el complemento «play-services.maps:v» sin más.
4. Al confirmar, tus dependencias se actualizarán y la app será sincronizada.
Esta es la forma de agregación por interfaz. Pero si ya has editado manualmente tu archivo build.gradle con anterioridad solo usa el siguiente comando compile
:
dependencies { ... compile 'com.google.android.gms:play-services-maps:8.4.0' }
2. Obtener El Fingerprint SHA-1 De Tu App
La siguiente instancia es crear un nuevo proyecto en la consola para desarrolladores de Google, la cual permitirá habilitar el servicio web en los servidores de Google para proveer datos desde la api de Google Maps.
Pero cuando llegues allí y vayas a crear las credenciales para tu app Android, se te pedirá una clave que determina la existencia real de esta.
Su nombre es fingerprint (huella dactilar «digital») SHA-1.
Haz lo siguiente para obtenerla:
1. Localiza el archivo con la keystore usada para una app en modo debug. La documentación específica que dicho archivo se denomina debug.keystore y se encuentra en:
- OS X y Linux:
~/.android/
- Windows Vista y Windows 7:
C:Usersnombre_usuario.android
En mi caso abro la terminal de Android Studio y navego hasta el directorio con el comando cd de la siguiente forma:
Me ubicó en la unidad C:
y luego navego con:
cd "C:/Users/Hermosa Programación/.android."
Usa las comillas para el nombre del directorio, esto te evitará problemas por espacios y caracteres especiales.
2. Usa la utilidad keytool
para extraer la huella digital SHA-1 con el siguiente comando:
keytool -list -v -keystore "debug.keystore" -alias androiddebugkey -storepass android -keypass android
En mi caso la consola me arroja el siguiente print:
Deja la consola abierta o salva el texto para el siguiente paso.
3. Crear Nuevo Proyecto En Google Console Developers
Para usar una api de Google es necesario crear un nuevo proyecto en Google Console Developers. Obviamente esto requiere que tengas una cuenta Google antes de manipular tu espacio.
La consola para desarrolladores de Google es una plataforma que te da acceso a la manipulación de proyectos web que implementarán una api. Desde allí podrás ver las apis propiedades y estadísticas de uso de cada proyecto.
Veamos cómo hacerlo.
1. Ingresa a tu Google Console Developers.
2. Selecciona el menú desplegable en la parte derecha de la toolbar que dice Selecciona un proyecto y selecciona Crear proyecto…
3. En el diálogo de creación asigna el nombre Google Maps En Android y confirma.
Si quieres puedes presionar Editar para cambiar el identificador de proyecto, por si tienes alguna convención de nombrado, ya que luego no será posible.
Al terminar la creación tendrás una notificación en la toolbar y el nuevo proyecto será seleccionado para administración.
4. En la lista de apis populares busca la sección de APIs de Google Maps y selecciona la opción Google Maps Android API.
5. En la visión general del servicio presiona Habilitar.
Cuando el servicio esté activado se te advertirá que es necesario una clave de api para que tu app Android pueda hacer peticiones al servicio de Google Maps. Presiona Ir a las credenciales para obtener una.
6. En el siguiente asistente selecciona Google Maps Android API como api destino y en la segunda Android. Confirma este par con el botón ¿Qué credenciales necesito?
7. Usa el nombre Clave Google Maps En Android para la clave de API y restringe el alcance del servicio a la aplicación que vas a usar, añadiendo el nombre del paquete y huella digital SHA-1.
El paquete puedes obtenerlo de tu AndroidManifest.xml y la huella digital solo es la copia que guardaste en la sección anterior. En mi caso quedaría así:
Lo siguiente es confirmar la creación de la clave.
8. Finalmente la consola te entregará una clave de API para usar en tu proyecto Android Studio.
Al presionar Listo podrás ver la sección Credenciales con tu nueva clave.
4. Instalar Google Play Services En Emulador
Si vas a usar tu móvil físico como fuente de pruebas de tu app no tendrás problemas para ejecutar tu app ya que muy rara vez los servicios de Google no estarán instalados.
Sin embargo los emuladores requieren una característica extra.
Google Play Services En AVDs de Android Studio
Para crear un AVD habilitado para GPS haz lo siguiente:
1. Abre el AVD Manager a través del siguiente icono:
2. En el administrador que salió presiona el botón Create Virtual Device…
3. Ahora te saldrá una lista con varios modelos de hardware para tu emulador. Dependiendo del tipo de dispositivo (TV, Wear, Phone o Tablet) que necesites así mismo elige la configuración que más te convenga.
En esta ocasión usaremos un Nexus 5X. Luego confirma con Next.
4. Ahora es el turno de la imagen del sistema. Ubícate en la pestaña Recommended y elige la imagen con la versión que más te convenga.
Solo fíjate que el valor de la columna Target exprese la existencia de las apis de Google (with Google APIs). Con esto te aseguras de tener Google Play Services en tu AVD.
Si tu equipo corre es de arquitectura x86 y corre a 64 bits, entonces fíjate en elegir una imagen con el valor de su columna ABI en x86_64, de lo contrario usa x86.
Confirma presionando Next.
5. Finalmente verifica las características de tu emulador y cambia aquellas de las que te retractes. O aprovecha para presionar Show Advance Settings y configurar propiedades de hardware como memoria, almacenamiento, núcleos, etc.
Confirma con Finish y espera que aparezca la lista de AVDs con tu nuevo emulador.
Google Play Services en Genymotion
Para los que usamos Genymotion, existen varias construcciones de Google play Services que pueden ser flasheadas en el emulador que tengamos. Los siguientes son recursos muy buenos:
- Installing Google Play Services in Genymotion Emulator
- Google Play Services with Android 6.0 Marshmallow and Genymotion
- How To Install Genymotion With Google Play Services APK
Como ejemplo te mostraré los pasos que seguí para instalar GPS en una imagen de Genymotion de un Nexus 5X con Android 6.0 (API 23).
Sigue estos pasos:
1. Descargar el adaptador Genymotion ARM Translation y arrastrarlo hacia el emulador en ejecución. Espera que se carguen los datos:
Cuando se complete, confirma con OK el flasheo del paquete.
Reinicia el emulador de inmediato.
2. Descarga las Google APIs para Android 6.0 y realiza el mismo procedimiento. Arrastra y suelta. Confirmar y reinicia el emulador.
3. Ve al menú de aplicaciones de Android, selecciona Google y agrega una cuenta.
4. Ahora descarga la compilación de Google Apps Benzo y aplica el mismo procedimiento de flasheo.
5. Con ello ya puedes ejecutar tu app con Google Play Services. Incluso ir a la Play Store para descargar las demás apps de Google.
5. Agregar Clave De API A la Aplicación
El funcionamiento de Google Maps en Android requiere que especifiques como meta información la clave asociada de Google Maps en la construcción de tu app.
Veamos.
1. Abre tu archivo AndroidManifest.xml y agrega un hijo <meta-data>
al nodo <application>
con la siguiente descripción:
<meta-data android:name="com.google.android.geo.API_KEY" android:value="TU_CLAVE_DE_API" />
Solo copia y pega la clave que obtuvimos en el paso anterior
<meta-data android:name="com.google.android.geo.API_KEY" android:value="AIzaSyC60P9Z53FX2kyqwJrvk2e_KhdtK6-cUPc" />
Con ello la app queda autorizada para realizar peticiones al servicio de Google Maps creado.
6. Añadir Un Mapa A La Aplicación
Ingredientes:
- Añadir
MapFragment
oSupportMapFragment
- Realizar transacción de fragmentos con Fragment Manager
- Implementar
OnMapReadyCallback
en la actividad y sobrescribeonMapReady()
- Llamar a
getMapAsync()
Para desplegar datos de Google Maps API usaremos clase MapFragment
en versiones mayores o igual a 12. Para el soporte de antecesores usa SupportMapFragment
.
Este fragmento administra totalmente la creación, actualización y destrucción de los mapas en la vista.
Por otro lado, las respuestas web serán manejadas por OnMapReadyCallback
. Esta escucha implementa el modelo asíncrono de los servicios de Google, para avisarte en qué momento el mapa está listo y te entrega una referencia del mismo que puedes manipular.
Preparación:
1. Ve al menú File > New > Fragment > Fragment (Blank) y crea un fragmento llamado FirstMapFragment.
No le agregues layout ni interfaz de comunicación.
Incluir un layout no es necesario ya que MapFragment
tiene una forma de creación interna.
La interfaz de comunicación no será necesaria porque en este ejemplo no enviaremos eventos desde el fragmento hacia la actividad.
Cuando tengas la nueva clase creada, hazla heredar de SupportMapFragment
. Luego sobrescribe onCreateView()
para que la vista sea inflada desde la superclase con super
.
Con ello tendrás:
import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.google.android.gms.maps.SupportMapFragment; /** * Muestra el mapa */ public class FirstMapFragment extends SupportMapFragment { public FirstMapFragment() { } public static FirstMapFragment newInstance() { return new FirstMapFragment(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = super.onCreateView(inflater, container, savedInstanceState); return root; } }
Otra manera: Añadir un nodo <fragment>
Otra forma alternativa para añadir un fragmento de mapas es usar en el layout de la actividad una etiqueta <fragment>
cuya referencia sea MapFragment
o SupportMapFragment
.
En nuestro caso se añade el siguiente contenido a activity_first_map.xml:
<?xml version="1.0" encoding="utf-8"?> <fragment 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="match_parent" tools:context=".FirstMapActivity" />
Hazlo de esta forma si el manejo del contenido del mapa es sencillo. Pero si deseas sobrescribir propiedades, comportamientos e información, será mucho más útil tener la clase del fragmento desligada del layout.
2. Añade un fragmento dinámicamente con el Fragment Manager.
Solo obtén una instancia relacionada al contexto con getSupportFragmentManager()
y realiza una transacción add()
en onCreate()
de la actividad.
public class FirstMapActivity extends AppCompatActivity { private FirstMapFragment mFirstMapFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first_map); mFirstMapFragment = FirstMapFragment.newInstance(); getSupportFragmentManager() .beginTransaction() .add(R.id.map_container, mFirstMapFragment) .commit(); } }
3. Implementa en FirstMapActivity
la interfaz OnMapReadyCallback
y sobrescribe su controlador onMapReady()
.
Recuerda que puedes usar Alt + Insert en Android Studio para generar el cuerpo de los métodos obligatorios a implementar. Solo selecciona la opción Implement Methods…
El código de la actividad debe quedarte así:
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; public class FirstMapActivity extends AppCompatActivity implements OnMapReadyCallback { private FirstMapFragment mFirstMapFragment; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first_map); mFirstMapFragment = FirstMapFragment.newInstance(); getSupportFragmentManager() .beginTransaction() .add(R.id.map_container, mFirstMapFragment) .commit(); } @Override public void onMapReady(GoogleMap googleMap) { } }
Ejecuta y verás el mapa.
4. Aunque el mapa está cargando, onMapReady()
no está siendo llamado debido a la ausencia de asociación.
Para ello, usa el método de asociación getMapAsync()
para registrar tu actividad como escucha.
onCreate()
es un buen lugar para hacerlo:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_first_map); mFirstMapFragment = FirstMapFragment.newInstance(); getSupportFragmentManager() .beginTransaction() .add(R.id.map_container, mFirstMapFragment) .commit(); // Registrar escucha onMapReadyCallback mFirstMapFragment.getMapAsync(this); }
Con esta acción onMapReady()
te proporcionará la instancia del mapa cuando el servicio web de Google Maps responda con el contenido.
A propósito. Este controlador recibe un parámetro GoogleMap.
Se puede decir que esta clase es la médula de toda la API, ya que representa el mapa como tal y te permitirá manejar los gráficos en el mapa, transformar la cámara, escuchar eventos, tomar instantáneas, etc.
Por ejemplo…
Añadir un marcador que apunte a la ciudad Cali en Colombia. Y mover la cámara sobre este punto
Solución:
Un marcador es un icono que apunta a una ubicación sobre el mapa (lo verás en más detalle en la siguiente sección). Para crear uno usa el método addMarker()
asociando la longitud y latitud de la ubicación.
El movimiento de cámara se realiza con moveCamera()
involucrando de nuevo la ubicación.
Así que dentro de onMapReady()
añade el siguiente código para probar el funcionamiento de la escucha. No te preocupes si no entiendes por el momento:
@Override public void onMapReady(GoogleMap googleMap) { LatLng cali = new LatLng(3.4383, -76.5161); googleMap.addMarker(new MarkerOptions() .position(cali) .title("Cali la Sucursal del cielo")); CameraPosition cameraPosition = CameraPosition.builder() .target(cali) .zoom(10) .build(); googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); }
Si ejecutas tendrás:
Guarda la instancia GoogleMap obtenida en onMapReady() en un campo global, que brinde futuros accesos.
7. Template Google Maps Activity En Android Studio
Android Studio trae consigo una plantilla para agilizar el uso de mapas en una actividad.
Para usarla sigue estos pasos:
1. Ubícate en tu paquete java y presiona Click derecho. Luego sigue la ruta New > Google > Google Maps Activity.
2. Configura el nombre, layout, titulo, jerarquía y paquete de la actividad en el asistente que Android Studio despliega.
3. Al confirmar con Finish, se creará la actividad con su layout y además se agregará la meta información necesario al AndroidManifest.xml.
Adicional a eso, extraerá tu clave de api en un archivo de recursos strings con el cualificador debug
llamado google_maps_api.xml.
<resources> <!-- TODO: Before you run your application, you need a Google Maps API key. To get one, follow this link, follow the directions and press "Create" at the end: https://console.developers.google.com/flows/enableapi?apiid=maps_android_backend&keyType=CLIENT_SIDE_ANDROID&r=0F:6B:C8:91:57:37:DE:8D:5C:73:39:C6:09:CE:CC:C3:84:BB:D5:83%3Bcom.herprogramacion.templategooglemapsactivity You can also add your credentials to an existing key, using this line: 0F:6B:C8:91:57:37:DE:8D:5C:73:39:C6:09:CE:CC:C3:84:BB:D5:83;com.herprogramacion.templategooglemapsactivity Alternatively, follow the directions here: https://developers.google.com/maps/documentation/android/start#get-key Once you have your key (it starts with "AIza"), replace the "google_maps_key" string in this file. --> <string name="google_maps_key" templateMergeStrategy="preserve" translatable="false">YOUR_KEY_HERE</string> </resources>
Solo reemplaza el contenido YOUR_KEY_HERE
por la clave entregada en Google console developers.
Puedes manejar la clave de api en otro archivo de recursos con el cualificador release
para cuando liberes tu app a la Play Store.
8. Gestos Móviles En El Mapa
Existen varios gestos que el usuario puede usar para cambiar la posición de la cámara en un mapa de Google Maps API. Con ello me refiero a desplazar el mapa, acercar/alejar, rotar y variar la inclinación.
Toma como base la siguiente ilustración de gestos móviles:
Zoom:
- Double tap para acercar
- Press and Tap para alejar
- Press and drag para acercar/alejar
- Spread para acercar
- Pinch para alejar
Desplazamiento:
- Drag para desplazar continuamente el mapa de forma omnidireccional
- Flick para desplazar en pequeñas porciones el mapa de forma omnidireccional
Inclinación:
- Drag x 2 dedos de forma vertical. Hacia arriba inclina hasta 90° y hacia abajo declina a 0°
Rotación:
- Rotate para rotar el mapa a favor o en contra de las manecillas del reloj.
Multi-touch en Genymotion
Usa los siguientes comandos en Genymotion para simular gestos multi-touch:
Multi-touch en AVDs
La versión 2.0 de Android Studio trajo consigo un soporte para gestos multi-touch bastante sencilla.
Los comandos son:
- Double tap: Doble click
- Drag/Flitch : Click izquierdo sostenido
- Pinch/Spread : Ctrl+ click izquierdo sostenido de adentro hacia a fuera o viceversa
- Rotate: Ctrl + click izquierdo sostenido siguiendo una circunferencia
- Press and tap: Ctrl + click derecho sostenido con movimiento vertical.
Habilitar/Deshabilitar gestos de UI
Si deseas restringir el acceso de gestos hacia el usuario usa la clase UiSettings
.
Esta configura las características de interfaz de usuario de la clase GoogleMap
.
Para obtener su instancia del mapa cargado usa getUiSettings()
y ten en cuenta los siguientes métodos relacionados a los gestos:
setZoomGesturesEnabled(boolean)
: Modifica la disponibilidad de los gestos de zoom. Usa false para deshabilitarlos.setScrollGesturesEnabled(boolean)
: Realiza exactamente que el método anterior pero para los gestos de desplazamiento.setTiltGesturesEnabled(boolean)
: Igual pero para la inclinación.setRotateGesturesEnabled(boolean)
: La disponibilidad en gestos de rotación.setAllGesturesEnabled(boolean)
: Modifica la disponibilidad de todos los gestos al mismo tiempo.
Por ejemplo…
Desactivar todos los gestos de desplazamiento e inclinación y mover la cámara a Estados Unidos
@Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; UiSettings uiSettings = mMap.getUiSettings(); uiSettings.setScrollGesturesEnabled(false); uiSettings.setTiltGesturesEnabled(false); mMap.animateCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(40.3839, -100.9565), 2)); }
9. Controles de UI Sobre El Mapa
Los controles del mapa son views de acceso rápido para una facilitar acción o gesto. Podemos ver algunos de ellos en la siguiente imagen:
El propósito de cada uno es:
- Controles de zoom: Dos botones en la parte inferior derecha para acercar y alejar. Estos vienen deshabilitados por defecto.
- Barra de tareas del mapa: Dos accesos que se muestran al tocar un marcador en la parte inferior derecha. Su función es dirigirte a la app de Google Maps.
- Botón «Mi ubicación»: Botón ubicado en la parte superior derecha que se habilita cuando activas la capa la capa «My Location». Al ser presionado anima la cámara a la ubicación actual (punto azul) que marque tu dispositivo si es que hay una.
- Brújula: Icono en la parte superior izquierda que aparece cuando la orientación e inclinación no son 0°. Si la presionas la cámara se moverá a su posición estándar.
Si quieres modificar la disponibilidad de cada elemento, usa la instancia UiSettings
del mapa y llama a uno de los siguientes métodos:
Controlador | Método |
---|---|
Zoom | setZoomControlsEnabled(boolen) |
Brujula | setCompassEnabled(boolean) |
Botón «Mi ubicación» | setMyLocationEnabled(boolean) , setMyLocationButtonEnabled(boolean) |
Barra de herramientas del mapa | setMapToolbarEnabled(boolean) |
Añadir botón de ubicación
1. El botón de ubicación requiere permisos de ubicación en nuestra app. Así que añade la siguiente línea a tu AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
En caso de que tu target sea Android Marshmallow hacia adelante debes preguntarle al usuario por los permisos en tiempo de ejecución.
2. Usa setMyLocationEnabled()
con el valor de true
.
public class ControlsActivity extends AppCompatActivity implements OnMapReadyCallback { private static final int LOCATION_REQUEST_CODE = 1; private GoogleMap mMap; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_controls); SupportMapFragment mapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mapFragment.getMapAsync(this); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; // Controles UI if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) { mMap.setMyLocationEnabled(true); } else { if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { // Mostrar diálogo explicativo } else { // Solicitar permiso ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_REQUEST_CODE); } } mMap.getUiSettings().setZoomControlsEnabled(true); // Marcadores mMap.addMarker(new MarkerOptions().position(new LatLng(0, 0))); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == LOCATION_REQUEST_CODE) { // ¿Permisos asignados? if (permissions.length > 0 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION) && grantResults[0] == PackageManager.PERMISSION_GRANTED) { mMap.setMyLocationEnabled(true); } else { Toast.makeText(this, "Error de permisos", Toast.LENGTH_LONG).show(); } } } }
10. Markers O Marcadores en Google Maps
Como viste en la sección anterior, un Marker es un objeto interactivo que marca una ubicación específica en la superficie del mapa, para representar un lugar, establecimientos, objetos, etc.
Cuando un marker es presionado, lo normal es que aparezca una ventana de información (Info Window) que muestra información relacionada al marker y que la cámara se posicione en la ubicación del marker.
Aunque la posición donde se encuentre puedes fijarla con coordenadas, también es posible dejar que el usuario los arrastre y suelte en ubicaciones personalizadas.
A continuación veremos los casos más populares sobre su tratamiento.
Agregar un marker
Para añadir un marcador se usa el método addMarker()
.
public final Marker addMarker (MarkerOptions options)
Este método recibe un objeto MarkerOptions
. Este actúa como un fabricador de markers basado en las propiedades que tú especifiques.
Su retorno es de tipo Marker
, la clase que representa como tal el marker en pantalla.
Ejemplo…
Añadir un marker en japón (36.2048, 138.2529). Usar la etiqueta «Japón» y la descripción «Primer ministro: Shinzō Abe»
Solución:
Crea un MarkerOptions
y usa:
position()
title()
snippet()
Luego añade la definición con addMarker()
en onMapReady()
;
@Override public void onMapReady(GoogleMap googleMap) { // Coordenadas Japón LatLng latLng = new LatLng(36.2048, 138.2529); MarkerOptions markerOptions = new MarkerOptions() .position(latLng) .title("Japón") .snippet("Primer ministro: Shinzō Abe"); Marker marker = googleMap.addMarker(markerOptions); }
Al ejecutar y presionar el marker tendrás:
Personalizar un marker
La sección de Markers en Google Maps API nos muestra las propiedades posibles que puedes manipular para que tus marcadores cambien su aspecto. Veamos una tabla resumen de estas:
Propiedad | Descripción | Método en MarkerOptions |
---|---|---|
Posición | Coordenadas de latitud y longitud para mover el marker en grados decimales. Se representa por la clase LatLng . |
position() |
Anchor | Punto del icono del marcador que representará la ubicación. Dicho punto se representa por coordenadas de textura (u,v) El punto A de la ilustración anterior muestra la ubicación por defecto. |
anchor() |
Alpha | Grado de opacidad del marcador.0 es transparente y 1 es color total. |
alpha() |
Título | Texto principal ubicado en la ventana de información | title() |
Snippet | Text secundario ubicado por debajo del título | snippet() |
Icono | Bitmap que representa al marker. Puedes cambiarlo por un recurso propio o usar el icono por defecto. | icon() |
Arrastre | Atributo booleano que define si un marker puede ser arrastrado por el usuario. Usa true para habilitarlo, o false (por defecto) en caso contrario. |
draggable() |
Visibilidad | Determina si el marker es visible (true ) o invisible (false ) |
visible() |
Geometría | Determina si el icono del marker se transformará (true ) con respecto a la posición de la cámara o se dibujará exactamente en la misma posición sin importar qué (false ). |
flat() |
Rotación | Especifica la rotación del marcador en orden de las manecillas del reloj. | rotation() |
Apliquemos la tabla anterior para algunos ejemplos:
Ejemplo: Cambiar color del marker a Cyan.
Pensamiento 1. Si observas la tabla, el método a usar es icon()
.
public MarkerOptions icon (BitmapDescriptor icon)
Este recibe un parámetro BitmapDescriptor
.
Esta clase define una imagen para los mapas en la API. La construcción de instancias se facilita a través de la clase fabricadora BitmapDescriptorFactory
.
Pensamiento 2. Para cambiar el color del icono actual usaremos el método de clase BitmapDescriptorFactory.defaultMarker()
:
public static BitmapDescriptor defaultMarker (float hue)
Donde hue debe tener por preferencia un valor de color establecido en las siguientes constantes:
float HUE_AZURE
float HUE_BLUE
float HUE_CYAN
float HUE_GREEN
float HUE_MAGENTA
float HUE_ORANGE
float HUE_RED
float HUE_ROSE
float HUE_VIOLET
float HUE_YELLOW
Deducción: Usar HUE_CYAN
con el método defaultMarker()
.
@Override public void onMapReady(GoogleMap googleMap) { LatLng japon2 = new LatLng(36.2048, 138.2529); googleMap.addMarker(new MarkerOptions() .position(japon2) .title("Marcador CYAN") .icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_CYAN))); googleMap.moveCamera(CameraUpdateFactory.newLatLng(japon2)); }
Resultado
Ejemplo: Cambiar icono del marker
1. Descarga o crea un recurso gráfico para tu marker.
2. Ve al proyecto en Android Studio y añade el recurso en diferentes densidades en tu carpeta drawable.
3. Ahora desde onMapReady()
crea el marcador y usa en las opciones el método icon()
.
Para producir la imagen puedes ayudarte de los siguientes métodos de clase:
fromAsset(String assetName)
: Genera unBitmapDescriptor
a partir de una imagen en la carpeta assetsfromBitmap(Bitmap image)
: Aquí la generación es a partir de un objeto BitmapfromFile(String fileName)
: Se genera desde el nombre de la imagen en el almacenamiento internofromPath(String absolutePath)
: Se genera desde la ruta absoluta de la imagen en el almacenamientofromResource(int resourceId)
: Se genera desde los recursos del proyecto
Debido a que incluimos la imagen en drawable, usarás fromResource()
con el id del recurso.
@Override public void onMapReady(GoogleMap googleMap) { LatLng position = new LatLng(10, 10); googleMap.addMarker(new MarkerOptions() .position(position) .title("Marcador con icono personalizado") .icon(BitmapDescriptorFactory.fromResource(R.drawable.marker_icon_pointer))); googleMap.moveCamera(CameraUpdateFactory.newLatLng(position)); }
Mi recurso se llama marker_icon_pointer.png
y produce el siguiente resultado:
Ejemplo: Hacer draggable un marker
1. Viendo la tabla resumida de propiedades sabemos que el método draggable() con true permitirá que el usuario arrastre el marcador a su preferencia.
Con esto el código quedaría así:
@Override public void onMapReady(GoogleMap googleMap) { LatLng position = new LatLng(15, 15); googleMap.addMarker(new MarkerOptions() .position(position) .title("Marcador draggable") .draggable(true)); googleMap.moveCamera(CameraUpdateFactory.newLatLng(position)); }
¿Cómo arrastro el marker?
Solo mantén presionado el marker por un instante y luego muévelo a cualquier posición al soltar la presión.
11. Manejar Eventos De Markers
La API de Google Maps provee escuchas de eventos sobre la clase GoogleMap
para procesar los siguientes gestos en un marcador:
Acción del usuario | Escucha responsable | Controladores |
---|---|---|
Clickear el marker | OnMarkerClickLister |
onMarkerClick(Marker marker) |
Clickear la Info Window | OnInfoWindowClickListener |
onInfoWindowClick(Marker marker) |
Arrastrar el marker | OnMarkerDragListener |
onMarkerDrag(Marker marker) onMarkerDragEnd(Marker marker) onMarkerDragStart(Marker marker) |
Practiquemos su uso con algunos ejemplos…
Eventos al clickear un marcador
Iniciar una actividad nueva cuando se presione un marcador. Dicha actividad debe mostrar la longitud y latitud.
Solución
1. Implementa la escucha OnMarkerClickListener
sobre FirstMapActivity
y crea un marker global llamado markerPais
.
public class MarkersActivity extends AppCompatActivity implements OnMapReadyCallback, GoogleMap.OnMarkerClickListener { private Marker markerPais;
... }
2. Dentro de onMapReady()
crea un marker que apunte a tu país y asignalo a markerPais
.
@Override public void onMapReady(GoogleMap googleMap) { // Markers LatLng colombia = new LatLng(4.6,-74.08); markerPais = googleMap.addMarker(new MarkerOptions() .position(colombia) .title("Colombia") ); // Cámara googleMap.moveCamera(CameraUpdateFactory.newLatLng(colombia)); // Eventos }
3. Usa setOnMarkerClickListener()
para asignar la actividad como escucha de clicks a través del operador this
.
// Eventos googleMap.setOnMarkerClickListener(this);
4. Sobrescribe el método onMarkerClick()
dentro de la actividad, para iniciar otra actividad llamada MarkerDetailActivity
.
Cuando crees la nueva actividad, abre su layout (activity_marker_detail.xml) y ubica el siguiente diseño:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.herprogramacion.googlemapsenandroid.MarkerDetailActivity"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="(%$1s, %$2s)" android:textSize="24sp" android:id="@+id/tv_latlng" android:layout_centerVertical="true" android:layout_centerHorizontal="true" /> </RelativeLayout>
Ahora desde onMarkerClick()
en FirstMapActivity
, extrae la latitud y longitud del marker entrante. Luego envíalas como extras en el nuevo Intent implícito de creación para MarkerDetailActivity
.
@Override public boolean onMarkerClick(Marker marker) { if (marker.equals(markerPais)) { Intent intent = new Intent(this, MarkerDetailActivity.class); intent.putExtra(EXTRA_LATITUD, marker.getPosition().latitude); intent.putExtra(EXTRA_LONGITUD, marker.getPosition().longitude); startActivity(intent); } return false; }
Aspectos a tener en cuenta del código anterior:
- Usa una sentencia de condiciones (
if
oswitch
) para determinar si el marker entrante tiene la misma referencia del que deseas. - Si debes crear marcadores en tiempo real, usa ids. Las colecciones de la clase
Map
son una buena solución en este caso. - El método
getPosition()
deMarker
permite obtener la posición con la que se creó.latitude
ylongitude
son los valores de las coordenadas respectivamente.
5. Obtén los datos desde MarkerDetailActivity
y setealos en el TextView
de la vista.
import android.content.Intent; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.widget.TextView; public class MarkerDetailActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_marker_detail); getSupportActionBar().setDisplayHomeAsUpEnabled(true); // Extraer lat. y lng. Intent intent = getIntent(); String latlng = String.format( getString(R.string.marker_detail_latlng), intent.getDoubleExtra(FirstMapActivity.EXTRA_LATITUD, 0), intent.getDoubleExtra(FirstMapActivity.EXTRA_LONGITUD, 0)); // Poblar TextView coordenadas = (TextView) findViewById(R.id.tv_latlng); coordenadas.setText(latlng); } @Override public boolean onSupportNavigateUp() { onBackPressed(); return super.onSupportNavigateUp(); } }
Si ejecutas tendrás un flujo similar a este:
El fragmento de mapas que tenemos creado también puede implementar las escuchas de click y procesar los eventos.
Eventos al arrastrar un Marker
Mostrar un Toast cuando el usuario comience o termine de arrastrar un marcador en Ecuador. Mientras siga siendo arrastrado, cambiar el título de la toolbar por las coordenadas actuales.
Solución
1. Implementa OnMarkerDragListener
sobre FirstMapActivity
.
2. Al igual que en el ejemplo pasado, dentro de onMapReady()
relaciona el objeto GoogleMap
con la escucha. Para ello usa setOnMarkerDragListener()
.
Recuerda usar el método draggable()
con true
para dejar arrastrar el marcador.
LatLng ecuador = new LatLng(-0.217, -78.51); markerEcuador = googleMap.addMarker(new MarkerOptions() .position(ecuador) .title("Ecuador") .draggable(true) ); // Cámara googleMap.moveCamera(CameraUpdateFactory.newLatLng(ecuador)); // Eventos googleMap.setOnMarkerDragListener(this);
3. Sobrescribe los controladores onMarkerDrag()
, onMarkerDragStart()
y onMarkerDragEnd()
.
onMarkerDrag()
se ejecuta cuando se comienza el arrastre, así que aquí crea un Toast con el mensaje "START"
.
@Override public void onMarkerDragStart(Marker marker) { if (marker.equals(markerEcuador)) { Toast.makeText(this, "START", Toast.LENGTH_SHORT).show(); } }
onMarkerDrag()
se consecutivamente llamado al arrastrar el marker. Por lo que aquí actualiza el título de la toolbar con las coordenadas.
@Override public void onMarkerDrag(Marker marker) { if (marker.equals(markerEcuador)) { String newTitle = String.format(Locale.getDefault(), getString(R.string.marker_detail_latlng), marker.getPosition().latitude, marker.getPosition().longitude); setTitle(newTitle); } }
R.string.marker_detail_latlng
es una string formateada para mostra el par latitud-longitud:
<string name="marker_detail_latlng">(%1$.2f, %2$.2f)</string>
onMarkerDragEnd()
se llama al soltar el marker. Ejecuta un Toast con el mensaje "END"
:
@Override public void onMarkerDragEnd(Marker marker) { if (marker.equals(markerEcuador)) { Toast.makeText(this, "END", Toast.LENGTH_SHORT).show(); } }
Ejecuta la app y prueba la visibilidad de las señales anteriores:
Eventos sobre la Info Window
Iniciar un diálogo con información extendida al presionar la ventana de información de un marker que apunte en Argentina.
Solución
1. Implementa sobre la actividad la escucha OnInfoWindowClickListener
y crea un marker global llamado markerArgentina
.
2. En onMapReady()
crea el marker para Argentina y registra la escucha con setOnInfoWindowClickListener()
:
LatLng argentina = new LatLng(-34.6, -58.4); markerArgentina = googleMap.addMarker( new MarkerOptions() .position(argentina) .title("Argentina") ); // Cámara googleMap.moveCamera(CameraUpdateFactory.newLatLng(argentina)); // Eventos googleMap.setOnMarkerClickListener(this); googleMap.setOnMarkerDragListener(this); googleMap.setOnInfoWindowClickListener(this);
3. Sobrescribe el método onInfoWindowClick()
con la creación de un nuevo diálogo.
Esto requiere que crees una nueva clase que extienda de DialogFragment
. La idea es generar un diálogo de alerta con título y mensaje.
Llama la clase ArgentinaDialogFragment
y pega el siguiente código:
import android.app.Dialog; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.DialogFragment; import android.support.v7.app.AlertDialog; public class ArgentinaDialogFragment extends DialogFragment { public static final String ARGUMENTO_TITLE = "TITLE"; public static final String ARGUMENTO_FULL_SNIPPET = "FULL_SNIPPET"; private String title; private String fullSnippet; public ArgentinaDialogFragment() { } public static ArgentinaDialogFragment newInstance(String title, String fullSnippet) { ArgentinaDialogFragment fragment = new ArgentinaDialogFragment(); Bundle b = new Bundle(); b.putString(ARGUMENTO_TITLE, title); b.putString(ARGUMENTO_FULL_SNIPPET, fullSnippet); fragment.setArguments(b); return fragment; } @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); Bundle args = getArguments(); title = args.getString(ARGUMENTO_TITLE); fullSnippet = args.getString(ARGUMENTO_FULL_SNIPPET); } @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Dialog dialog = new AlertDialog.Builder(getActivity()) .setTitle(title) .setMessage(fullSnippet) .create(); return dialog; } }
Ahora desde onInfoWindowClick()
crea una nueva instancia del diálogo. Añade el título del marker y asigna un recurso string con datos de Argentina:
@Override public void onInfoWindowClick(Marker marker) { if (marker.equals(markerArgentina)) { ArgentinaDialogFragment.newInstance(marker.getTitle(), getString(R.string.argentina_full_snippet)) .show(getSupportFragmentManager(), null); } }
donde R.string.argentina_full_snippet
tiene el siguiente extracto de wikipedia en strings.xml:
<string name="argentina_full_snippet"> La República Argentina, conocida simplemente como Argentina, es un país soberano de América del Sur, ubicado en el extremo sur y sudeste de dicho subcontinente. Adopta la forma de gobierno republicana, representativa y federal. El Estado nacional convive federativamente con veinticuatro entidades estatales autónomas, de las cuales veintitrés son provincias que preservan todo el poder no delegado constitucionalmente a la Nación y una es la Ciudad Autónoma de Buenos Aires, designada por ley como capital federal. </string>
Resultado:
Borrar Marker
La clase Marker
nos proporciona el método remove()
para borrar el marker del objeto GoogleMap
. Tan solo llámalo desde la instancia a eliminar y listo:
markerArgentina.remove();
No mostrar la InfoWindow al presionar un marker
En la documentación de la escucha de clicks en los markers nos recomiendan que si deseamos evitar que se muestre la info window, solo debemos hacer retornar el método onMarkerClick()
en true
.
Por ejemplo, el marker de Colombia al ser presionado nos envía a otra actividad, sin embargo la ventana de información se muestra.
La modificación es sencilla:
@Override public boolean onMarkerClick(Marker marker) { if (marker.equals(markerColombia)) { Intent intent = new Intent(this, MarkerDetailActivity.class); intent.putExtra(EXTRA_LATITUD, marker.getPosition().latitude); intent.putExtra(EXTRA_LONGITUD, marker.getPosition().longitude); startActivity(intent); return true; } return false; }
Ahora, esta modificación no posiciona la cámara en el marker como normalmente vemos.
Para solucionar este incidente, usa tu instancia global del mapa con el método animateCamera()
.
Puedes añadir una escucha CancellableCallback
para ejecutar la actividad solo cuando termine la animación (más adelante veremos más detalles).
@Override public boolean onMarkerClick(final Marker marker) { if (marker.equals(markerColombia)) { map.animateCamera(CameraUpdateFactory.newLatLng(marker.getPosition()), new GoogleMap.CancelableCallback() { @Override public void onFinish() { Intent intent = new Intent(MarkersActivity.this, MarkerDetailActivity.class); intent.putExtra(EXTRA_LATITUD, marker.getPosition().latitude); intent.putExtra(EXTRA_LONGITUD, marker.getPosition().longitude); startActivity(intent); } @Override public void onCancel() { } }); return true; } return false; }
11. Tipos De Mapas En La API
Hasta ahora se ha visto una sola representación gráfica de los mapas que has visto en ejemplos.
Sin embargo hay más diseños que varían el detalle y estructura.
Resumiéndolos serían:
Tipo | Contenido | Constante asociada |
---|---|---|
Normal | Carreteras y objetos naturales como vegetación o ríos | MAP_TYPE_NORMAL |
Híbrido | Datos de carreteras junto a capturas satelitales | MAP_TYPE_HYBRID |
Satélite | Capturas de satélites | MAP_TYPE_SATELLITE |
Tierra | Detalles de relieve en el terreno y datos sobre carreteras | MAP_TYPE_TERRAIN |
Ninguno | Una cuadrícula vacía | MAP_TYPE_NONE |
Para asignar el tipo de mapa programáticamente usa el método setMapType()
con alguna de las constantes anteriores como parámetro.
Ejemplo: Cambiar tipo de mapa
Proporciona un Spinner al usuario para que cambie el tipo de mapa entre las 5 opciones disponibles
Solución
1. El layout de FirstMapActivity
debe modificarse para ubicar un spinner en la parte superior y por debajo tener el mapa.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical" android:padding="@dimen/activity_horizontal_margin"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Tipo de mapa" android:textAppearance="@style/TextAppearance.AppCompat.Caption" android:textColor="@color/colorPrimary" /> <Spinner android:id="@+id/map_type_selector" android:layout_width="match_parent" android:layout_height="?attr/listPreferredItemHeight" android:entries="@array/map_types_list" /> </LinearLayout> <fragment android:id="@+id/map" android:name="com.google.android.gms.maps.SupportMapFragment" android:layout_width="match_parent" android:layout_height="match_parent" /> </LinearLayout>
Recuerda que el atributo android:entries
recibe un recurso <string-array>
con la lista de las opciones que tendrá el spinner. Ya sabemos que estas deben ser los 5 tipos de mapas declaradas así:
strings.xml
<string-array name="map_types_list"> <item>None</item> <item>Normal</item> <item>Satellite</item> <item>Hybrid</item> <item>Terrain</item> </string-array>
2. Ahora ve a la actividad y realiza lo siguiente:
- Declara una instancia global para el map
- Obtén en
onCreate()
el Spinnermap_type_selector
y busca el fragmento con el Fragment Manager - Implementa sobre la actividad
OnItemSelectedListener
y relacionala con elSpinner
. - Implementa
OnMapReadyCallback
- Llama a
getMapAsync()
y guarda la referenciaGoogleMap
.
Con ello tendrás un código como el siguiente:
import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.AdapterView; import android.widget.Spinner; import com.google.android.gms.maps.GoogleMap; import com.google.android.gms.maps.OnMapReadyCallback; import com.google.android.gms.maps.SupportMapFragment; public class FirstMapActivity extends AppCompatActivity implements OnMapReadyCallback, AdapterView.OnItemSelectedListener { private SupportMapFragment mMapFragment; private GoogleMap mMap; private Spinner mMapTypeSelector; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_map_types); mMapTypeSelector = (Spinner) findViewById(R.id.map_type_selector); mMapTypeSelector.setOnItemSelectedListener(this); mMapFragment = (SupportMapFragment) getSupportFragmentManager() .findFragmentById(R.id.map); mMapFragment.getMapAsync(this); } @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; } @Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { } @Override public void onNothingSelected(AdapterView<?> parent) { } }
3. Lo siguiente es usar el método setMapType()
dentro de onItemSelected()
.
Como sabes que el orden de los ítems del spinner no variará, entonces puedes crear un arreglo de enteros para relacionar las constantes de tipo a la posición seleccionada.
private int mMapTypes[] = { GoogleMap.MAP_TYPE_NONE, GoogleMap.MAP_TYPE_NORMAL, GoogleMap.MAP_TYPE_SATELLITE, GoogleMap.MAP_TYPE_HYBRID, GoogleMap.MAP_TYPE_TERRAIN };
Con esta variable podemos usar el tercer parámetro position
de onItemSelected()
de la siguiente forma:
@Override public void onItemSelected(AdapterView<?> parent, View view, int position, long id) { mMap.setMapType(mMapTypes[position]); }
Ejecuta la app y prueba los tipos:
12. Mover La Cámara Del Mapa
La cámara es el punto de vista que refleja una cantidad de espacio o volumen en los mapas de la API de Google Maps.
En ejemplos anteriores vimos métodos para cambiar la posición y realizar zoom. La idea ahora es ver más características y utilidades que complementen tu conocimiento.
Las transformaciones de la cámara se representan con CameraUpdate
y para producir instancias de esta usa CameraUpdateFactory
.
Cambiar nivel de zoom
Los siguientes son métodos de clase de CameraUpdateFactory
que modifican el zoom.
Método | Descripción |
---|---|
zoomIn() |
Aumenta el zoom en 1.0 |
zoomOut() |
Disminuye el zoom en 1.0 |
zoomTo(float zoom) |
Modifica el zoom desde el valor actual hasta el proporcionado |
zoomBy(float amount) |
Aumenta o disminuye (número negativo) el zoom en las unidades que desees. |
Por ejemplo…
Aumentemos el zoom en 20 unidades al iniciar el mapa:
@Override public void onMapReady(GoogleMap googleMap) { googleMap.moveCamera(CameraUpdateFactory.zoomBy(20)); }
Cambiar objetivo de la cámara
En las secciones anteriores movimos varias veces la cámara con nuevas coordenadas de latitud y longitud.
Pero veamos un resumen de los métodos que tenemos para hacerlo:
Método | Descripción |
---|---|
newLatLng(LatLng latLng) |
Genera una instancia CameraUpdate que ubica el objetivo de la cámara en las nuevas coordenadas |
newLatLngZoom(LatLng latLng, float zoom) |
Hace lo mismo que newLatLng() y adicionalmente le combina un nivel de zoom. |
newLatLng()
ya lo vimos grandes cantidades de veces, así que hagamos un ejemplo con su variante.
Cambiar la ubicación hacia Nicaragua y aumentar 7 niveles de zoom.
La solución sería la siguiente:
@Override public void onMapReady(GoogleMap googleMap) { LatLng nicaragua = new LatLng(13, -85); googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(nicaragua, 7)); }
Desplazar la cámara
También es posible crear instancias de CameraUpdate
para mover en n pixeles las coordenadas de longitud y latitud, con respecto a la ubicación actual.
Esta transformación se logra con CameraUpdateFactory.scrollBy(float x, float y)
donde,
- Los valores positivos en
x
desplazan la cámara hacia la derecha. Los negativos hacia la izquierda. - Los valores positivos en
y
desplazan la cámara hacia abajo. Los negativos hacia arriba.
Por ejemplo…
Un desplazamiento de 100 pixeles a la derecha y 200 hacia arriba se da por.
mMap.moveCamera(CameraUpdateFactory.scrollBy(100,-200));
Personalizar la posición de la cámara
Si deseas cambiar todas las propiedades relacionadas a la posición de la cámara usa la clase CameraPosition
.
Puedes crear una instancia con el operador new
de forma normal o usar CameraPosition.Builder
para facilitar el proceso.
La edición debes realizarla basado en los siguientes atributos:
float bearing
: Orientación vertical medida en grados partiendo desde el norte en sentidos de las manecillas del relojLatLng target
: La ubicación donde está apuntando la cámarafloat tilt
: Separación en ángulos desde la orientación de la cámara hasta el nadir.float zoom
: Nivel del zoom con respecto a la cámara
Cuando ya tengas preparado tu objeto, entonces usa el método CameraUpdateFactory.newCameraPosition()
con el fin de crear un nuevo elemento CameraUpdate
basado en tus argumentos.
Por ejemplo…
Ubicar la cámara apuntando a España, 7 niveles de zoom, alineada verticalmente 90° respecto al norte y con una inclinación de 90°
El código sería:
LatLng españa = new LatLng(40.416667, -3.75); CameraPosition cameraPosition = new CameraPosition.Builder() .target(españa) .zoom(7) .bearing(90) .tilt(90) .build(); googleMap.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition));
El resultado:
Mover la vista de la cámara
Actualizar la posición de la cámara en el mapa se logra a través de dos métodos de GoogleMap
: moveCamera()
y animateCamera()
.
Ambos aplican el objeto CameraUpdate
para actualizar el objetivo actual de la cámara en la vista. La diferencia es que animateCamera()
anima el movimiento.
¿Cómo usar moveCamera()?
Esto ya lo viste varias veces. Solo produces un nuevo CameraUpdate
con CameraUpdateFactory
y lo pasas como parámetro.
LatLng cali = new LatLng(3.444, -76.511); googleMap.moveCamera(CameraUpdateFactory.newLatLng(cali));
¿Cómo usar animateCamera()?
Exactamente igual que el anterior:
googleMap.animateCamera(CameraUpdateFactory.newLatLng(cali));
Sin embargo este método tiene una sobrecarga muy útil para controlar la duración de la animación y una escucha relacionada.
void animateCamera (CameraUpdate update, int durationMs, GoogleMap.CancelableCallback callback)
donde,
update
: Nuevo transformación de posición de la cámaradurationMs
: La cantidad de milisegundos que durará la animacióncallback
: Escucha para determinar cuándo termina la animación o cuando se cancela prematuramente.
La instancia de CancelableCallback
debe tener definido dos métodos:
onFinish()
: Se llama si la animación termino sin percances.onCancel()
: Se llama si la animación terminó prematuramente.
Por ejemplo…
Animar el cambio de posición de la cámara hacia las coordenadas (80,80) al presionar el action button «Inicio». Al terminar la animación desplegar un Toast con un mensaje de confirmación.
Solución…
1. Crea un nuevo action button programáticamente en la actividad con onPrepareOptionsMenu()
. Usa el título «Inicio»:
@Override public boolean onPrepareOptionsMenu(Menu menu) { menu.add("Inicio").setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); return super.onPrepareOptionsMenu(menu); }
2. Procesa el evento de este elemento en onOptionsItemSelected()
:
@Override public boolean onOptionsItemSelected(MenuItem item) { CharSequence title = item.getTitle(); if (title != null && title.equals("Inicio")) { // Animar cámara } return super.onOptionsItemSelected(item); }
3. Establece un nuevo LatLng
en (80,80) para usar newLatLng()
en animateCamera()
como primer parámetro.
La duración ponla 2000 ms y crea una instancia anónima de CancelableCallback
en el tercer parámetro.
Sobrescribe el controlador onFinish()
para crear un Toast
que indique que la animación terminó:
@Override public boolean onOptionsItemSelected(MenuItem item) { CharSequence title = item.getTitle(); if (title != null && title.equals("Inicio")) { LatLng zero = new LatLng(80, 80); mMap.animateCamera( CameraUpdateFactory.newLatLng(zero), // update 2000, // durationMs new GoogleMap.CancelableCallback() { // callback @Override public void onFinish() { Toast.makeText(CameraActivity.this, "Animación finalizada", Toast.LENGTH_LONG).show(); } @Override public void onCancel() { } }); } return super.onOptionsItemSelected(item); }
Al ejecutar tendrás:
13. Crear Formas Con Google Maps API
Existen tres clases para crear formas en tus mapas:
Polyline
: Conjunto de líneas interconectadas. Útiles al marcar rutas.Polygon
: Figura que permite encerrar áreas del mapa. Útiles para mostrar localidades o lugares de incidenciaCircle
: Figura circular dibujada sobre el mapa.
Veamos como producir cada forma…
Crear polilíneas
Para crear una polilínea solo necesitas saber la latitud y longitud donde comienza y termina un segmento de línea.
Ingredientes:
- La clase
PolylineOptions
- La clase
LatLng
- La clase
Polyline
- El método
GoogleMap.addPolyline()
Preparación:
1. La clase PolylineOptions
facilita la creación de instancias Polyline
. Solo añade vértices terminales LatLng
con el método add()
.
Ten en cuenta el orden de la secuencia para ir creando los segmentos rectos.
// Ejemplo: Delimitar a Sudamérica con un rectángulo PolylineOptions sudamericaRect = new PolylineOptions() .add(new LatLng(12.897489, -82.441406)) // P1 .add(new LatLng(12.897489, -32.167969)) // P2 .add(new LatLng(-55.37911, -32.167969)) // P3 .add(new LatLng(-55.37911, -82.441406)) // P4 .add(new LatLng(12.897489, -82.441406)) // P1 .color(Color.parseColor("#f44336")); // Rojo 500
2. Usa el método addPolyline()
para añadir la instancia PolylineOptions
previamente creada.
// Instancia Polyline para posteriores usos Polyline polyline = mMap.addPolyline(sudamericaRect);
3. (Opcional) Mueve la cámara para visualizar la polilínea:
// Mover cámara mMap.animateCamera(CameraUpdateFactory.newLatLng(new LatLng(-20.96144, -61.347656)));
El resultado de la unión de los puntos dados es el siguiente:
Crear polígonos
Un polígono se representa en el mapa como una figura plana limitada por al menos 3 vértices y ángulos. Aunque su método de creación es casi igual que Polyline
, este tiene un color de relleno.
Ingredientes:
- La clase
PolygonOptions
- La clase
LatLng
- La clase
Polygon
- El método
GoogleMap.addPolygon()
Preparación:
1. Investiga, obtén o produce los vértices del polígono y crea un objeto LatLng
por cada uno:
// Ejemplo: Encerrar a Cuba con un polígono de bajo detalle LatLng p1 = new LatLng(21.88661065803621, -85.01541511562505); LatLng p2 = new LatLng(22.927294359193038, -83.76297370937505); LatLng p3 = new LatLng(23.26620799401109, -82.35672370937505); LatLng p4 = new LatLng(23.387267854439315, -80.79666511562505); LatLng p5 = new LatLng(22.496957602618004, -77.98416511562505); LatLng p6 = new LatLng(20.20512046753661, -74.16092292812505); LatLng p7 = new LatLng(19.70944706110937, -77.65457527187505);
2. Usa la clase PolygonOptions
para predefinir instancias de Polygon
.
Solo añade vértices LatLgn
con su método add()
y ten en cuenta que la API está diseñada para autocompletar el polígono y producir una figurada cerrada.
Polygon cubaPolygon = mMap.addPolygon(new PolygonOptions() .add(p1, p2, p3, p4, p5, p6, p7, p1) .strokeColor(Color.parseColor("#AB47BC")) .fillColor(Color.parseColor("#7B1FA2")));
3. Usa la el objeto Polygon
retornado por addPolygon()
para posteriores usos y cambios. Por ejemplo, con Polygon.setPoints()
puedes modificar los vértices de nuevo.
4. (Opcional) Actualiza la cámara
mMap.animateCamera(CameraUpdateFactory.newLatLngZoom( new LatLng(21.5034305704608, -78.95096199062505), 5));
El ejemplo anterior nos muestra el polígono sobre Cuba:
Crear círculos
Usa la clase Circle
para representar un círculo en la superficie del mapa con relleno. Su construcción se basa en la definición de un centro y el radio.
Ingredientes:
- La clase
CircleOptions
- La clase
LatLng
- La clase
Circle
- El método
GoogleMap.addCircle()
Preparación:
1. Define el centro del círculo con un objeto LatLng
y su radio con un flotante en metros.
// Ejemplo: Crear círculo con radio de 40m // y centro (3.4003755294523828, -76.54801384952702) LatLng center = new LatLng(3.4003755294523828, -76.54801384952702); int radius = 40;
2. Crea un objeto CircleOptions
para configurar el círculo. Ten en cuenta los siguientes métodos:
center(LatLng center)
: Asigna el valor del centroradius(double radius)
: Asigna el radiostrokeColor(int color)
: Asigna el color de la línea de trazo. Usa la clase de utilidadesColor
para crear colores o usar constantes comoColor.RED
.strokeWidth(float width)
: Asigna el grueso de la línea de trazofillColor(int color)
: Asigna el color del relleno del círculo.
Estos te permitirán personalizar la forma como se ve en la continuación del ejemplo:
CircleOptions circleOptions = new CircleOptions() .center(center) .radius(radius) .strokeColor(Color.parseColor("#0D47A1")) .strokeWidth(4) .fillColor(Color.argb(32, 33, 150, 243));
3. Añade el círculo con GoogleMap.addCircle()
. Si deseas modificar en el futuro el círculo, entonces toma la referencia de la instancia Circle
que retorna este método.
// Añadir círculo Circle circle = mMap.addCircle(circleOptions);
4. (Opcional) Actualiza el objetivo de la cámara:
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(center, 17));
El resultado será la delimitación del Parque del Refugio en mi ciudad Cali.
14. Manejar Eventos Sobre El Mapa
¿Qué tipos de eventos recibe un objeto GoogleMap
y que escucha uso para procesarlos?
Evento | Escucha |
---|---|
Click | OnMapClickListener |
Click prolongado | OnMapLongClickListener |
Cambio de cámara | OnCameraChangeListener |
Mapas de interiores | OnIndoorStateChangeListener |
Marcadores | Ver sección de marcadores |
Ventanas de información | Ver sección de marcadores |
Veamos algunos ejemplos…
Procesar eventos de Click
Como se ve en la tabla anterior debes usar OnMapClickListener
para procesar el evento de la vista (Actividad o Fragmento). Para usarla veremos los siguientes pasos.
Ingredientes:
Activity
oFragment
OnMapClickListener
y su controladoronMapClick()
GoogleMap.setOnMapClickListener()
LatLng
Projection
Preparación:
1. Implementa la interfaz OnMapClickListener
sobre tu vista.
En nuestro caso usaremos FirstMapActivity
. Así que solo usamos implements
y sobrescribimos onMapClick()
.
public class EventsActivity extends AppCompatActivity implements OnMapReadyCallback, GoogleMap.OnMapClickListener { private GoogleMap mMap; // Cuerpo @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; } @Override public void onMapClick(LatLng latLng) { } }
2. Asigna la escucha con setOnMapClickListener()
en onMapReady()
@Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; // Eventos mMap.setOnMapClickListener(this); }
3. Procesa onMapClick()
y realiza las acciones correspondientes con su parámetro LatLng
.
Aquí nuestro ejemplo imprime un mensaje con las coordenadas.
@Override public void onMapClick(LatLng latLng) { String format = String.format(Locale.getDefault(), "Lat/Lng = (%f,%f)", latLng.latitude, latLng.longitude); Toast.makeText(this, format, Toast.LENGTH_LONG).show(); }
4. (Opcional) Traduce las coordenadas a pixeles de la pantalla con la clase Projection
y su método toScreenLocation()
.
Como variante, concatenamos los pixeles reales al mensaje a imprimir.
@Override public void onMapClick(LatLng latLng) { String formatLatLng = String.format(Locale.getDefault(), "Lat/Lng = (%f,%f)", latLng.latitude, latLng.longitude); Point screentPoint = mMap.getProjection().toScreenLocation(latLng); String formatScreenPoint = String.format(Locale.getDefault(), "nPoint = (%d,%d)", screentPoint.x, screentPoint.y); Toast.makeText(this, formatLatLng + formatScreenPoint, Toast.LENGTH_LONG).show(); }
El resultado sería:
Procesar eventos de click prolongado
El click prolongado se refiere al gesto Press, que se da cuando el usuario presiona un punto de la pantalla por un tiempo extendido. Y como dice la tabla se requiere OnMapLongClickListener
para leer esta acción.
Ingredientes:
Activity
oFragment
OnMapLongClickListener
y su controladoronMapLongClick()
GoogleMap.setOnMapLongClickListener()
LatLng
Projection
Preperación:
1. Implementa OnMapLongClickListener
sobre el Fragmento u Actividad.
public class EventsActivity extends AppCompatActivity implements OnMapReadyCallback GoogleMap.OnMapLongClickListener { private GoogleMap mMap; // Cuerpo @Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; } @Override public void onMapLongClick(LatLng latLng) { } }
2. Relaciona el mapa con la escucha a través de setOnMapLongClickListener().
El ejemplo necesita los gestos deshabilitados para el propósito del ejercicio.
@Override public void onMapReady(GoogleMap googleMap) { mMap = googleMap; // Configuración UI mMap.getUiSettings().setAllGesturesEnabled(false); // Eventos mMap.setOnMapLongClickListener(this); }
3. Incluye las acciones necesarias en onMapLongClick(LatLng)
para procesar la ubicación.
Añadiremos un marcador en la posición donde se hizo click prolongado. Si su coordenada x en la pantalla es menor o igual que el ancho total de esta, entonces cambiaremos su color a Amarillo. De lo contrario será Naranja.
@Override public void onMapLongClick(LatLng latLng) { // Añadir marker en la posición Marker marker = mMap.addMarker(new MarkerOptions().position(latLng)); // Obtener pixeles reales Point point = mMap.getProjection().toScreenLocation(latLng); // Determinar el ancho total de la pantalla DisplayMetrics display = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(display); int width = display.widthPixels; float hue; // ¿La coordenada de pantalla es menor o igual que la mitad del ancho? if (point.x <= width / 2) { hue = BitmapDescriptorFactory.HUE_YELLOW; } else { hue = BitmapDescriptorFactory.HUE_ORANGE; } // Cambiar color del marker según el grupo marker.setIcon(BitmapDescriptorFactory.defaultMarker(hue)); }
Comienza a presionar puntos y tendrás marcadores separados:
Conclusión
¿Cómo te fue con este inicio en Google APIs?
Espero que hayas probado una buena cantidad de características de Google Maps.
Todos los temas explicados en este tutorial satisfacen aplicaciones que deseen destacar ubicaciones, mostrar datos importantes sobre lugares, encerrar áreas, crear marcadores personalizados para diferenciar entidades, etc.
Por lo que puedes complementar muy bien con otros elementos UI de Android para solucionar necesidades de tus usuarios.
Si deseas mostrar ubicaciones con información más detallada, rastrear de forma precisa un elemento, calcular distancias y tiempos de viaje o trazar caminos por direcciones transitables, entonces investiga sobre los Web Services de Google Maps.