Añade Maps SDK a tu proyecto
De manera similar a como añadíamos JavaSDK a un proyecto, podemos añadir Maps SDK. Simplemente pon la siguiente dependencia en tu build.gradle.
implementation 'com.mapbox.maps:android:10.16.4'

Ada, está muy contenta con el avance el del videojuego que servirá para promocionar prendas deportivas de una marca. Sólo queda abordar la última parte del juego: los sensores y la geolocalización. La empresa ve muy interesante lanzar ofertas atractivas proponiendo retos sencillos al usuario de forma que si alcanza una serie de puntos se puedan canjear por un producto de la empresa o bien sirvan como descuento. Además quieren conocer la ubicación actual de un cliente y poder publicitar sus tiendas según la cercanía del cliente.
Ada comunica a su equipo Juan y Ana que es importante conocer los sistemas de posicionamiento global GPS, conocer los sensores más importantes con los que cuenta un móvil (acelerómetro, giroscopio, brújula etc) teniendo en cuenta en todo momento la privacidad de los clientes.
Juan ve muy interesante que a partir de la medición de movimiento o cambio de posición del dispositivo se pueda crear una experiencia en el juego que sea divertida, pero desconoce muchos aspectos. No obstante se siente preparado para afrontar este nuevo reto.
GPS son las siglas de Sistema de Posicionamiento Global y se trata de un servicio que se proporciona a los usuarios a través de un dispositivo para tener información sobre su posición, la velocidad a la que se mueve y otros datos como su altura en cualquier momento.
Creado con eXeLearning (Ventana nueva)
Juan quiere reunirse con su equipo para tener claro qué enfoque dar al último encargo de la empresa. Es consciente que un teléfono Android cuenta con una serie de sensores pero hay que tener claro cuáles son los más frecuentes y para qué sirven.
En la conversación con Ana recuerdan los primeros teléfonos que salieron al mercado y como los teclados físicos dieron paso a los botones táctiles y éstos a la barra clásica de navegación. Hoy en día son muchos los usuarios que utilizan la navegación por gestos en móviles más modernos y se proponen conocer más fondo todas las opciones que dispone un usuario para interactuar con un dispositivo móvil.
En primer lugar hay que saber que un método de entrada o Input Method es una parte del sistema operativo o un programa que pemite al usuario introducir carácteres y símbolos en una aplicación. Con el comienzo de los ordenadores se sustituyó la escritura a mano que era demasiado lenta, por el teclado. Aunque hay varios sistemas de teclados el más usado en los ordenadores es el sistema QWERTY heredado de las máquinas de escribir y cuyo nombre viene de las primeras seis letras de casi todos los teclados del mundo. Este sistema se creó con el fin de separar las teclas que aparecen con más frecuencia de forma que se incrementase la velocidad de escritura.
Con la aparición de los primeros teléfonos se tiene un teclado físico alfanumérico y fue el único método de interacción con nuestros móviles durante mucho tiempo. Estos teléfonos no sólo permitían realizar llamadas sino que se podía escribir mensajes a golpe de click ya que cada tecla contenía varios caracteres alfabéticos y se tenían que seleccionar uno a uno. Si es cierto que el método de entrada mejoró con los sistemas de texto predictivo y no hacía falta pulsar varias veces, pero la entrada al mercado de dispositivos móviles con teclado físico QWERTY o teclado completo facilitó mucho la tarea.
Lo más frecuente era que el móvil fuera plegable y el teclado se deslizara por la parte trasera del teléfono cambiando la orientación de la pantalla de modo vertical a horizontal, este fue el caso del primer dispositivo Android en 2008, más conocido en los EE.UU. como el T-Mobile G1. Otra opción fue que el teclado físico se ubicara en la parte inferior de la pantalla de forma que el frontal es compartido por la pantalla y el teclado, siendo BlackBerry la empresa insignia. Estos dispositivos ya eran táctiles pero había un inconveniente con estos teclados completos y es que ocupaban un espacio, que poco a poco, se necesitaba para mostrar más contenido en las pantallas.
De ahí que las pantallas táctiles fueron la mejor opción para ampliar la superficie de la pantalla las cuales ofrecen nuevas experiencias al usuario pulsando la pantalla o bien haciendo uso de los gestos o Gestures, donde con uno o varios dedos se puede ejecutar distintas acciones, todo en la misma superficie. Con las pantallas táctiles aparecieron los teclados virtuales QWERTY. La escritura en estos teclados era complicada ya que el tamaño inicial de estos teclados era insuficiente, de 3 a 4 pulgadas, y las aplicaciones no eran tan avanzadas pero actualmente las pantallas táctiles han crecido y escribir en ellas no resulta tan complicado.
Existen aplicaciones específicas para la entrada de datos como como Google Keyboard integrada en Android que incorpora nuevos avances: soporte de múltiples idiomas, dictado por voz, predicción de palabras, introducción de caracteres por gestos etc.
En los siguientes apartados se estudiará todo lo relacionado con las entradas del usuario, desde entrada táctil y gestos básicos, a como controlar las entradas del teclado.
Es un gesto táctil que se el usuario realiza con uno o más dedos en una pantalla táctil de forma que una aplicación recopila todos los eventos táctiles y los comprara con patrones que admite la aplicación para ver si coincide con un gesto específico.
Creado con eXeLearning (Ventana nueva)
Teniendo en cuenta los diferentes métodos de entrada que se han visto, ahora como programadores tenemos la misión de interceptar los eventos que ocurren cuando el usuario interacciona con nuestra aplicación. Este evento se lanza desde un objeto de vista específico y se espera que un objeto de escucha o EventListener asociado a ese componente realice una determinada acción. Estos componentes también reciben el nombre de oyentes.
Si accedes a la documentación oficial de esta clase verás que es una interfaz de la clase View que contiene un método de devolución de llamada o callback que será llamado por el sistema cuando ocurra el evento sobre el componente.
Un escuchador responde a la interacción del usuario sobre un objeto vista cuando haya sido registrado o asociado al objeto vista.
Un objeto vista puede registrar varios escuchadores, ten en cuenta que el sistema llamará al método oportuno dependiendo del evento. A continuación se muestra una tabla que contiene el método que se invoca cuando se produce un tipo de evento, la descripción del evento, la interfaz del objeto escuchador y un ejemplo de cómo conectar un componente al oyente :
| Método de la interfaz | Descripción | Interfaz | Método de asociación |
|---|---|---|---|
| onClick(View v) | Se llama a este método cuando el usuario pulsa (en modo táctil) sobre el objeto vista. | View.OnClickListener |
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Código a ejecutar en el hilo principal
}
});
|
| onLongClick(View view) | Se llama cuando se hace clic en una vista y se mantiene presionada durante más de un segundo. | View.OnLongClickListener |
textview.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
// Código a ejecutar en el hilo principal
}
});
|
| onFocusChange (View v, boolean hasFocus) | Se ejecuta cuando el elemento tiene el foco o bien sale de él. | View.OnFocusChangeListener |
edittext.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
// Código a ejecutar en el hilo principal
}
});
|
| onKey(View v, int keyCode, KeyEvent event) | Cuando el usuario presiona o suelta una tecla de hardware sobre un elemento. | View.OnKeyListener |
linearlayout.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
} else {
return false;
}
}
});
|
| onTouch(View v, MotionEvent event) | Cuando realiza una acción táctil (presionar, soltar o cualquier gesto) en los límites de un elemento. | View.OnTouchListener() |
imageButton.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(event.getAction() == MotionEvent.ACTION_UP){
// Código a ejecutar en el hilo principal
return true;
}
return false;
}
});
|
| onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) | Se llama a este método cuando se crea un menú contextual resultado de un "clic largo". | View.OnCreateContextMenuListener | |
Si te fijas en los métodos que se listan en la tabla, los métodos pueden recibir dos argumentos:
En ambos casos se puede invocar distintos métodos de cada uno de ellos para obtener información, por ejemplo conocer el ID de la vista mediante el método getId() o la tecla pulsada en KeyEvent mediante el método getKeyCode().
Otra peculiaridad de estos métodos es que dependiendo del evento devuelven un valor booleano al sistema para indicar si se usó el evento, en este caso hay que devolver el valor true o bien si no se quiere procesar el evento se devuelve false de forma que se propagará el evento a otros escuchadores de eventos. Ten en cuenta que puede haber controladores en la definición de la clase, de forma que, si queremos que no se propague, debemos asegurarnos que el evento finaliza con el valor verdadero.
Creado con eXeLearning (Ventana nueva)
En la asociación del componente con el oyente de eventos hay que indicar quién será el controlador del evento, es decir, el oyente llamará al método del controlador que será donde se programe las acciones a ejecutar. También suelen recibir el nombre de manejadores de eventos. En los ejemplos del apartado 1.1.- Escuchador de eventos se crea una clase anónima interna mediante el operador new seguido por la clase que desea ampliar o en el caso del siguiente ejemplo de la interfaz que se quiere implementar:
new View.OnClickListener() {
@Override
public void onClick(View v) {
// Código a ejecutar en el hilo principal
}
}
En este caso el objeto que actúe como controlador del evento no tiene asignado un nombre por tanto sólo se podrá usar una única vez dentro de un componente. Su uso es muy común con el oyente OnClickListener ya que cuando se pulsa un componente, por ejemplo un botón, las acciones a realizar en Aceptar o Cancelar son diferentes y por tanto sus controladores de eventos. Esta opción genera mucho código y se podría simplificar usando una de las opciones que explicamos a continuación:
Fragment implemente la interfaz, siguiendo con el mismo ejemplo el código es mucho más limpio como se muestra a continuación:public class MainActivity extends Activity implements OnClickListener{
@Override
protected void onCreate(Bundle savedValues) {
Button b = (Button)findViewById(R.id.boton);
b.setOnClickListener(this);
}
@Override
public void onClick(View v) {
//Acciones a realizar
}
....
}
A través del siguiente vídeo puedes aprender las alternativas de crear un controlador de eventos para un componente:
Creado con eXeLearning (Ventana nueva)

Actualmente los dispositivos Android no cuentan con un teclado físico sino que muestra un teclado software o Soft Keyboard en la pantalla, conocido como método de entrada táctil o IME. Este teclado aparece en pantalla cuando se pulsa sobre un campo de texto en la interfaz gráfica o bien cuando recibe el foco y su función es permitir al usuario introducir texto.
Puede darse el caso de tener un teclado hardware conectado al dispositivo o bien un dispositivo con controles de juegos o joystiks, SÓLO en estos casos se podrá controlar eventos de teclado con la claseKeyEvent y API relacionadas y el teclado software no aparecerá en la pantalla.
El comportamiento del sistema Android hace que si tenemos nuestra actividad en primer plano con un teclado software o método de entrada, GENERALMENTE NO activan la familia de eventos onKeyDown() con lo cual no debemos desarrollar una aplicación que requiera que se presionen teclas concretas. Algunos pueden optar por hacerlo en algunas situaciones, pero incluso si es así, el resultado pueda no ser el esperado por lo que no hay forma de detectar de forma fiable las pulsaciones de teclas.
Veamos a ver a continuación las opciones según el tipo de teclado:
En este caso se puede controlar la pulsación de cualquier tecla o bien la combinación de varias (Ctrl+A) como se explica en la sección Cómo administrar las acciones del teclado: El ejemplo más común es querer realizar una acción cuando se pulsa una tecla individual usando onKeyUp() que sólo se llama una vez ya que si el usuario mantiene el botón pulsado se llama a onKeyDown() varias veces. Las clases Activity y View implementan la interfaz KeyEvent.Callback, por lo que, en general, debes anular los métodos de devolución de llamada en nuestra clase Activity como se muestra a continuación:
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_D:
//Realizar acción
return true;
case KeyEvent.KEYCODE_F:
//Realizar acción;
return true;
case KeyEvent.KEYCODE_J:
//Realizar acción
return true;
case KeyEvent.KEYCODE_K:
//Realizar acción
return true;
default:
return super.onKeyUp(keyCode, event);
}
}
}
En el caso que desarrolles un juego y quieras que sea compatible con dispositivos para videojuegos el sistema Android registra los eventos de entrada como códigos de teclas y valores de ejes. En este supuesto tienes que saber qué control de entrada está conectado, qué tipos de eventos de entrada se pueden recibir, la compatibilidad entre las diferentes versiones de Android etc. Si quieres profundizar sobre el tema debes leer la sección Cómo administrar las acciones de los controles de la documentación oficial de Android.
Como ya se ha comentado no es fiable implementar la interfaz KeyEvent.Callback en una vista, un método de entrada de software no tiene la obligación de activar este oyente. Una posible solución sería usar el oyente TextWatcher que se activa cuando cambian los datos, pero si se quiere controlar que se ha pulsado por ejemplo la tecla DEL sin haber modificado el texto no se puede usar. La solución es utilizar la interfaz OnEditorActionListener() que permite capturar el evento dentro del método de entrada y a continuación se pregunta por la tecla pulsada:
edName.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEND || EditorInfo.IME_ACTION_UNSPECIFIED==actionId) {
Toast.makeText(MainActivity.this,"Se ha pulsado la tecla SEND",Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
En el ejemplo que mostramos, utilizamos el atributo android:imeOptions, que permite añadir opciones adicionales en el IME asociado, modificando la tecla Done por Send:
<EditText android:imeOptions="actionSend"> </EditText>
Además podemos configurar el método de entrada o IME en nuestra aplicación. En nuestra actividad podemos decidir cómo queremos que se comporte el método de entrada respecto a la ventana que lo contiene usando el atributo android:windowSoftInputMode en a declaración de la Activity en el fichero AndroidManifest.xml en base a dos aspectos:
stateHidden, stateAlwaysVisible, etc)adjustResize, y adjustPan)Se pueden utilizar más de un valor usando la barra vertical (|) como elemento separador. Por ejemplo: <activity android:windowSoftInputMode="stateVisible|adjustResize" ...>
Además se puede definir cómo queremos que se comporte el método de entrada en un campo de texto. Por ejemplo, si en un campo EditText se quiere introducir un número de teléfono se puede restringir el tipo de entrada a un serie de caracteres usando el valor phone dentro del atributo android:inputType. En la sección Cómo especificar el tipo de método de entrada de la documentacioń oficial puedes ver todas las opciones.
Te mostramos el ejemplo donde tenemos una Activity con dos campos de texto edName y edSurname. En este ejemplo puedes comprobar:
edName se muestra un mensaje emergente al usuario. No así en el campo edSurname que no tiene asignado ningún oyente.Descarga ejemplo KeyListenerEditText (zip - 16.63 MB).
Te mostramos a continuación otro ejemplo donde se muestra cómo ocultar el teclado cuando se pulsa una tecla del método de entrada:
Creado con eXeLearning (Ventana nueva)
La pantalla de los dispositivos móviles es sensible al tacto, eso significa que es capaz de detectar un contacto o múltiples puntos de contacto con la superficie. Con este tipo de eventos podremos detectar cuándo el usuario interacciona con la pantalla, ya que el sistema llama al método onTouchEvent(android.view.MotionEvent) de la interfaz onTouchListener. Este método recibe como parámetro un objeto de la clase MotionEvent que contiene la siguiente información del evento que se ha registrado:
getY().Tenemos que tener en cuenta que con la pantalla táctil que puede que en la jerarquía de vistas haya una vista que esté interesada en un gesto en curso. Si este fuera el caso se debe anular el método dispatchTouchEvent() en una Activity, o también en un objeto ViewGroup y View. El ejemplo que se muestra sería en una Activity:
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
break;
case MotionEvent.ACTION_POINTER_UP:
break;
case MotionEvent.ACTION_UP:
break;
case MotionEvent.ACTION_DOWN:
break;
}
return super.dispatchTouchEvent(ev);
}
}
Este método es un controlador que decide cómo enrutar los eventos táctiles. Para explicar este concepto enumeramos a continuación las llamadas que se realizan en una jerarquía de vistas con los eventos Touch:
ViewGroup que si no es interceptado mediante el método onInterceptTouchEvent() pasa a todas las vistas secundarias que puedan manejar el evento a través del método onTouchEvent().Activity.dispatchTouchEvent() y onInterceptTouchEvent() SÓLO se llamarán para MotionEvent.ACTION_DOWN. Sin un verdadero de onTouchEvent, la vista principal asumirá que su vista no necesita MotionEvents.Recuerda que todos los eventos Touch comienzan con ACTION_DOWN y terminan con ACTION_UP.
En el siguiente ejemplo se muestra cómo la Activity implementa el evento dispatchTouchEvent de forma que la vista TextView ejecuta el método onTouch(). Esto es posible porque el método de dispatchTouchEvent devuelve la llamada al framework de Android con return super.dispatchTouchEvent(event) de forma que propaga el evento a la vista inferior TextView. Comprueba cómo devolviendo el valor true la vista TextView no responde al evento.
Descarga del ejemplo TouchActivity (zip - 16.34 MB).
Creado con eXeLearning (Ventana nueva)
Cuando el usuario dibuja en la pantalla táctil, se forma una serie de trazos que es lo que se conoce como gesto o Gestures. Un gesto está formado por una secuencia de puntos de contacto con la pantalla o MotionEvents que contienen información de la acción realizada, dónde tuvo lugar el toque, la presión, etc. Posteriormente, el sistema puede interpretar estos datos y ver si cumple con el patrón de gestos que admite una aplicación. Android proporciona el espacio de nombres Android.Gestures específicamente para administrar y responder a los gestos.
El sistema de gestos se integró en Android 9 Pie con la barra de navegación de forma que deslizando la barra se puede acceder a la pantalla de inicio, al cajón de aplicaciones, a la vista de aplicaciones recientes y cambiar a una aplicación anterior de nuevo con un gesto, al asistente de Google etc.
Los gestos simplifican la interfaz de usuario evitando integrar un botón para cada acción, la barra de navegación ha eliminado el botón inicio, atrás y recientes ya que una vista puede desplazarse en varios sentidos. De forma que estudiaremos cómo integrar los gestos que han venido a quedarse ya que cada versión de Android tiene mejoras y nuevas opciones. Vamos a ver dos opciones:
onLongPress(), onFling() en objetos Activity o View e igualmente realizar una acción en nuestra aplicación.Falso
La rotación es un gesto que consta mínimo de dos puntos y a continuación un movimiento rotatorio sobre uno de los ejes x, y o z.
Creado con eXeLearning (Ventana nueva)

Te mostramos a continuación un ejemplo donde se crea una librería de gestos y posteriormente se utiliza en una aplicación:
Crear y usar una librería de gestos personalizados
Para entender este ejemplo explicamos a continuación las clases que se utilizan y la funcionalidad que tienen:
<android.gesture.GestureOverlayView android:id="@+id/gestures_overlay" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gestureStrokeType="multiple" />
private GestureLibrary library;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
library=GestureLibraries.fromRawResource(this,R.raw.gestures);
...
}
public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) {
ArrayList<Prediction> predictions = library.recognize(gesture);
if (predictions.size() > 0 && predictions.get(0).score> 1.0) {
String action = predictions.get(0).name;
Toast.makeText(this, action, Toast.LENGTH_SHORT).show();
}
}
Creado con eXeLearning (Ventana nueva)
Un gesto está formado por varios contactos con la pantalla que llamaremos a continuación punteros y que pueden darse al mismo tiempo. En un evento multitáctil haremos uso de métodos de la clase MotionEvent que nos permite acceder al conjunto de punteros como:
private int mActivePointerId;
public boolean onTouchEvent(MotionEvent event) {
...
// Get the pointer ID
mActivePointerId = event.getPointerId(0);
// ... Many touch events later...
// Use the pointer ID to find the index of the active pointer
// and fetch its position
int pointerIndex = event.findPointerIndex(mActivePointerId);
// Get the pointer's current position
float x = event.getX(pointerIndex);
float y = event.getY(pointerIndex);
...
}<br />
Hay que tener en cuenta también que en un evento multitáctil se desencadenan los siguientes eventos táctiles:
El mejor código de ejemplo se encuemtra en Android Developer bajo la licencia de Apache 2.0
int action = MotionEventCompat.getActionMasked(event);
// Get the index of the pointer associated with the action.
int index = MotionEventCompat.getActionIndex(event);
int xPos = -1;
int yPos = -1;
Log.d(DEBUG_TAG,"The action is " + actionToString(action));
if (event.getPointerCount() > 1) {
Log.d(DEBUG_TAG,"Multitouch event");
// The coordinates of the current screen contact, relative to
// the responding View or Activity.
xPos = (int)MotionEventCompat.getX(event, index);
yPos = (int)MotionEventCompat.getY(event, index);
} else {
// Single touch event
Log.d(DEBUG_TAG,"Single touch event");
xPos = (int)MotionEventCompat.getX(event, index);
yPos = (int)MotionEventCompat.getY(event, index);
}
...
// Given an action int, returns a string description
public static String actionToString(int action) {
switch (action) {
case MotionEvent.ACTION_DOWN: return "Down";
case MotionEvent.ACTION_MOVE: return "Move";
case MotionEvent.ACTION_POINTER_DOWN: return "Pointer Down";
case MotionEvent.ACTION_UP: return "Up";
case MotionEvent.ACTION_POINTER_UP: return "Pointer Up";
case MotionEvent.ACTION_OUTSIDE: return "Outside";
case MotionEvent.ACTION_CANCEL: return "Cancel";
}
return "";
}
Android proporciona la clase GestureDetector que permite consumir MotionEvents y crear eventos de gestos de mayor nivel para los oyentes. Se usaría para detectar gestos táctiles básicos como desplazar, lanzar o tocar dos veces seguidas. El framework de Android proporciona dos GesturesDetectors que se pueden usar:
¿Pero cómo funciona estos detectores dentro de una vista multitáctil? Dentro del método onTouch() de la Activity se llamaría a su método público onTouchEvent (MotionEvent) igual que el método de una vista o View devuelve true si maneja el evento y falso si no lo hace. Ya hemos dicho que GestureDectector informa de ciertos eventos de gestos específicos, para ello utiliza el escuchador OnGestureListener o bien SimpleOnGestureListener si no se quiere implementar todos sus métodos como el ejemplo que se muestra a continuación:
public class MainActivity extends Activity implements
GestureDetector.OnGestureListener,
GestureDetector.OnDoubleTapListener{
private static final String DEBUG_TAG = "Gestures";
private GestureDetectorCompat mDetector;
// Called when the activity is first created.
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Instantiate the gesture detector with the
// application context and an implementation of
// GestureDetector.OnGestureListener
mDetector = new GestureDetectorCompat(this,this);
// Set the gesture detector as the double tap
// listener.
mDetector.setOnDoubleTapListener(this);
}
@Override
public boolean onTouchEvent(MotionEvent event){
if (this.mDetector.onTouchEvent(event)) {
return true;
}
return super.onTouchEvent(event);
}
@Override
public boolean onDown(MotionEvent event) {
Log.d(DEBUG_TAG,"onDown: " + event.toString());
return true;
}
@Override
public boolean onFling(MotionEvent event1, MotionEvent event2,
float velocityX, float velocityY) {
Log.d(DEBUG_TAG, "onFling: " + event1.toString() + event2.toString());
return true;
}
@Override
public void onLongPress(MotionEvent event) {
Log.d(DEBUG_TAG, "onLongPress: " + event.toString());
}
@Override
public boolean onScroll(MotionEvent event1, MotionEvent event2, float distanceX,
float distanceY) {
Log.d(DEBUG_TAG, "onScroll: " + event1.toString() + event2.toString());
return true;
}
@Override
public void onShowPress(MotionEvent event) {
Log.d(DEBUG_TAG, "onShowPress: " + event.toString());
}
@Override
public boolean onSingleTapUp(MotionEvent event) {
Log.d(DEBUG_TAG, "onSingleTapUp: " + event.toString());
return true;
}
@Override
public boolean onDoubleTap(MotionEvent event) {
Log.d(DEBUG_TAG, "onDoubleTap: " + event.toString());
return true;
}
@Override
public boolean onDoubleTapEvent(MotionEvent event) {
Log.d(DEBUG_TAG, "onDoubleTapEvent: " + event.toString());
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent event) {
Log.d(DEBUG_TAG, "onSingleTapConfirmed: " + event.toString());
return true;
}
}<br />
Como puedes ver en el ejemplo la clase GestureDectector detecta los siguientes gestos simples:
Y.Y.>Si quieres ver un ejemplo de cómo usar ScaleGestureDector accede al siguiente tutorial Dar sentido al multitáctil.
Te mostramos a continuación un tutorial que describe cómo utilizar la API táctil en aplicaciones de Android con varios ejemplos que muestran cómo detectar múltiples punteros en una vista y el uso de la clase GestureDetector:
Creado con eXeLearning (Ventana nueva)
Uno de los mayores avances de los teléfonos inteligentes han sido los sensores que se han ido incorporando, de hecho, quitando la memoria y el procesador, los sensores que incluyen establecen una serie de prestaciones que engloban a un dispositivo en una gama baja, media o alta y dependerá de cada fabricante.
Además los sensores, entre otros aspectos, hace que un móvil se diferencie por ejemplo de un portátil. La mayoría de las funciones importantes de un móvil están asociadas a un sensor: ajustar el brillo de la pantalla, girar una imagen según estés mirando la pantalla en vertical o en horizontal, identificar una huella digital etc.
Si consultamos la definición de sensor, es un dispositivo capaz de detectar una magnitud física o química (temperatura, intensidad lumínica, distancia, aceleración, inclinación, desplazamiento, presión, fuerza, torsión, humedad, movimiento etc) y transformarla en una salida eléctrica que podrá ser interpretada por nuestro dispositivo. En el siguiente enlace puedes ver al menos 15 sensores y la función que tiene cada uno:
En este listado no se encuentran la cámara, ni el micrófono, ni el GPS, aunque dispongan de sensores para su función, ya que utilizan clases diferentes para su uso y utilización dentro de Android. Para el resto de sensores, Android utiliza el mismo marco de trabajo de forma que podremos realizar las siguientes acciones:
Los sensores se engloban en tres categorías:
Creado con eXeLearning (Ventana nueva)

Para trabajar con los sensores utilizaremos las siguientes clases:
Dentro de la clase Sensor vamos a destacar los siguientes método públicos que contiene:
Cuando se trabajan con sensores de movimiento o posición hay que tener en cuenta el sistema de referencia que está constituido por tres ejes perpendiculares entre sí (ejes XYZ). La posición, la escala y la rotación de un objeto se basa en este sistema, ya que se asigna un valor a cada eje. Por defecto el sistema de coordenadas se define en relación con la pantalla del dispositivo. Este sistema de coordenadas es el mismo independientemente de la orientación del dispositivo.
Algunos sensores y métodos utilizan el sistema de referencia relativo al mundo, es decir, muestran datos que representan el movimiento del dispositivo o la posición en relación con la tierra.
Por último debes leer el apartado Prácticas recomendadas para acceder a los sensores y usarlos de la documentación oficial para tener claro las pautas a seguir en la implementación de un sensor. Una pauta muy importante es desactivar los sensores que no se necesitan, especialmente cuando nuestra actividad está en pausa ya que el sistema no desactiva los sensores automáticamente cuando la pantalla se apague y están consumiendo batería cuando no se están usando.
Ya conocemos las clases a utilizar en nuestros proyectos y las consideraciones que tenemos que tener a la hora de trabajar con ellos. A continuación aprenderemos cómo recoger los datos de los sensores en un dispositivo real.
Creado con eXeLearning (Ventana nueva)

Lo primero que tenemos que hacer es obtener los sensores que tiene disponibles nuestro móvil mediante SensorManager.getSensorList(int) que devuelve una lista:
private TextView tvSensor;
private SensorManager sensorManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvSensor = (TextView) findViewById(R.id.tvSensor);
sensorManager = (SensorManager)
getSystemService(SENSOR_SERVICE);
List<Sensor> listSensor = sensorManager.
getSensorList(Sensor.TYPE_ALL);
for (Sensor sensor : listSensor) {
tvSensor.append(sensor.getName() + "\n");
}
}
<br />
El siguiente paso es obtener datos de un sensor en concreto para ello se debe crear un escuchador SensorEventListener. En este caso será nuestra aplicación quien reciba las notificaciones de los sensores cuando cambien por lo que al implementar dicha interfaz se crean dos métodos:
Inicialmente en el primer método onAccuracyChanged() no realizaremos ninguna acción con lo cual lo dejamos en blanco. Con el segundo método vamos a detectar un tipo de sensor y recoger valores del mismo:
@Override
public void onSensorChanged(SensorEvent event) {
switch (event.sensor.getType()) {
case Sensor.TYPE_ACCELEROMETER:
mAccelerometerData = event.values.clone();
for (int i = 0; i < 3; i++)
tvSensor.append("Accelerometer: " + i + ": " + event.values[i] + "\n");
break;
case Sensor.TYPE_MAGNETIC_FIELD:
mMagnetometerData = event.values.clone();
for (int i = 0; i < 3; i++)
tvSensor.append("Magnetometer: " + i + ": " + event.values[i] + "\n");
break;
default:
mOtherData = event.values.clone();
for (int i = 0; i < event.values.length; i++)
tvSensor.append("Other: " + i + ": " + event.values[i] + "\n");
}
}
<br />
Por cada sensor que se quiera utilizar se debe registrar su escuchador, en este caso la actividad representada por el objeto this:
listSensor = sensorManager.getSensorList(Sensor.TYPE_ACCELEROMETER);
if (!listSensor.isEmpty()) {
Sensor acelerometroSensor = listSensor.get(0);
sensorManager.registerListener(this, acelerometroSensor,
SensorManager.SENSOR_DELAY_UI);
}
listSensor = sensorManager.getSensorList(Sensor.TYPE_MAGNETIC_FIELD);
if (!listSensor.isEmpty()) {
Sensor magneticSensor = listSensor.get(0);
sensorManager.registerListener(this, magneticSensor,
SensorManager.SENSOR_DELAY_UI);
}
listSensor = sensorManager.getSensorList(Sensor.TYPE_PROXIMITY);
if (!listSensor.isEmpty()) {
Sensor proximidadSensor = listSensor.get(0);
sensorManager.registerListener(this, proximidadSensor,
SensorManager.SENSOR_DELAY_UI);
}
<br />
Finalmente se tiene que cancelar el registro de los objetos de escucha del sensor bien cuando se deje de utilizar o bien cuando se pause nuestra actividad:
...
@Override
protected void onPause() {
super.onPause();
sensorManager.unregisterListener(this);
}
<br />
A continuación puedes descargarte el código de ejemplo: SensorApplication (zip - 0.38 MB).
Hay muchos tipos de sensores y eventos, en el siguiente tutorial puedes ver un ejemplo de cómo usar el sensor de luz y proximidad.
Creado con eXeLearning (Ventana nueva)
Los sensores de movimiento son los más populares en los dispositivos móviles porque permiten monitorear el movimiento del dispositivo, como la inclinación, sacudida, rotación o balanceo. Muchas aplicaciones se basan en su funcionalidad:
Hay que tener en cuenta que la arquitectura es diferente según el tipo de sensor:
En el siguiente tutorial verás un ejemplo de cómo utilizar el sensor acelerómetro para medir la fuerza de aceleración en m/s2 en un dispositivo en los tres ejes físicos (x, y y z), incluida la fuerza de gravedad.
Ejemplo de acelerómetro de Android
En este ejemplo se utiliza SensorEvent.values que es una matriz para conocer la aceleración en cada uno de los ejes:
@Override
public void onSensorChanged(SensorEvent event) {
float xValue = event.values[0];
float yValue = event.values[1];
float zValue = event.values[2];
Log.d(LOG_TAG, "x:"+xValue +";y:"+yValue+";z:"+zValue);
}<br />
Hay que tener en cuenta que un cambio puede ser casi imperceptible para nuestros sentidos pero el acelerómetro registra cualquier movimiento por lo que se debe establecer un valor de umbral de forma que, si el movimiento es inferior a este umbral será considerado como ruido y no se tendrá en cuenta. Su valor se establece en la constante SHAKE THRESHOLD GRAVITY y puede ser un valor superior a 1 e inferior a 2 según la sensibilidad. Este valor es un múltiplo de la gravedad de la Tierra de ahí que en cada dirección se calcula la aceleración en base a la gravedad que se encuentra en la Tierra establecido en SensorManager.GRAVITY_EARTH.
@Override
public void onSensorChanged(SensorEvent sensorEvent) {
float x = sensorEvent.values[0];
float y = sensorEvent.values[1];
float z = sensorEvent.values[2];
float gX = x / SensorManager.GRAVITY_EARTH;
float gY = y / SensorManager.GRAVITY_EARTH;
float gZ = z / SensorManager.GRAVITY_EARTH;
double gForce = Math.sqrt(gX * gX + gY * gY + gZ * gZ);
if (gForce > SHAKE_THRESHOLD_GRAVITY) {
logD("Se ha realizado un movimiento");
}
}<br />
En esta sección se ha explicado cómo trabajar con el acelerómetro influenciado por la gravedad. No obstante hay muchos otros sensores que te pueden ser útiles y que se explican en la documentación oficial:
El metro por segundo al cuadrado está compuesta por las unidades básicas de longitud y la unidad estándar de tiempo. Se utiliza como medida del incremento de la velocidad conseguido cada segundo.
El siguiente proyecto muestra el uso de los sensores de acelerómetro y giroscopio en un dispositivo Android Wear. Puedes leer el siguiente tutorial Crear un emulador para Android Wear y ejecutar el siguiente código:
Creado con eXeLearning (Ventana nueva)
Juan y Ana ya saben cómo recoger valores de los sensores y aplicarlos en el juego. Están muy emocionados porque sólo les queda conocer la ubicación real de un cliente para poder notificar ofertas de la empresa. No es una tarea complicada ya que todos los teléfonos incorporan receptores de GPS además han utilizado aplicaciones de navegación, pero una cosa es usar una aplicación y otra saber cómo funciona e implementar la geolocalización en una aplicación.
Lo primero que tenemos que conocer es cómo se obtiene la posición de un objeto a nivel mundial, y es mediante un conjunto de 27 satélites que se encuentran en órbita sobre la tierra, 3 de los cuales funcionan como refuerzo. La posición de estos satélites permite que haya cobertura en cualquier sitio del planeta. Nuestro dispositivo tiene que tener un receptor GPS que permita conectarse al menos con tres de los satélites más cercanos, el cual calcula su posición en base al tiempo que tarda en llegar cada señal de cada satélite. Este método se conoce como trilateración. Aunque con tres satélites es suficiente para triangular tu posición en un mapa, también suele utilizarse la señal de un cuarto para determinar la altitud (distancia vertical).
Hay que tener en cuenta que el sensor GPS consume bastante batería y requiere estar en un entorno abierto. Como esto no siempre se cumple, se hace uso de la red de comunicaciones móviles o GSM formada por millones de torres y antenas que dan cobertura para llamar desde nuestros teléfonos. Nuestro dispositivo puede calcular su posición utilizando tres torres de telefonía para triangular su posición, de nuevo, en base al tiempo que tarda la señal en ir a la torre y la fuerza de la señal. Esta tecnología se llama sistema de posicionamiento global asistido o A-PGS.
Podemos ver cómo funciona la geolocalización de nuestro dispositivo mediante la aplicación Google Maps que proporciona mapas online. En primer lugar se consigue la posición mediante el sistema A-PGS mostrando la posición con un círculo azul en un radio aproximado de 20 metros. Si Google Maps no puede determinar la posición aparecerá un círculo de color azul claro alrededor del punto azul que desparecerá cuando se actulice la posición a través de los satélites GPS.
Si quieres conocer cómo funciona el GPS de un móvil y las opciones de configuración para mejorar la precisión de nuestro GPS te recomendamos la lectura del siguiente enlace:
Creado con eXeLearning (Ventana nueva)
A través de la clase LocationManager podemos acceder a los servicios de ubicación del sistema Android y pertenece al framework de Android (android.location). Esta clase proporciona tres tipos de proveedores de ubicación:
android.permission.ACCESS_FINE_LOCATION. Para utilizar este proveedor usaremos la constante NETWORK_PROVIDER de la clase LocationManagerSe puede obtener el servicio de localización como se ha realizado en otras ocasiones mediante el método getSystemService():
LocationManager locationManager =(LocationManager) this.getSystemService(Context.LOCATION_SERVICE);
A continuación se puede obtener la lista de proveedores mediante el método getAllProviders() que devuelve el listado de proveedores conocidos por el dispositivo, independientemente de si tenemos permiso o si están desactivados. A través de esta lista se puede acceder a propiedades como su precisión, coste, consumo, altitud, velocidad...
List listProvider = locationManager.getAllProviders();
LocationProvider provider = locationManager.getProvider(listProvider.get(0));
int precision = provider.getAccuracy();
boolean supportsAltitude = provider.supportsAltitude();
int requirement = provider.getPowerRequirement();
Con los servicios de Google Play aparece el proveedor de ubicación fusionada o Fused Location Provider. Este proveedor utiliza GPS según la precisión o el intervalo de actualización que se necesite pero fusiona la información de ubicación en base a otros proveedores de red, sensores, historial de ubicaciones etc. En este caso utilizaremos la clase FusedLocationProviderClient que es el punto de interacción con los servicios de Google Play, en nuestro caso para usar Google Maps.
La ventaja de este tipo de proveedor es que optimiza los recursos y Google lo está mejorando para añadir más fuentes de datos. Este proveedor permite través de los servicios de Google notificar al usuario cuando entra o sale de un área llamada geovallado, se puede calcular nuestra posición en base a otros objetivos (restaurantes, gasolineras, tiendas...), utiliza diferentes sensores del dispositivo para saber si un usuario está caminando, andando en bicicleta, conduciendo un automóvil o simplemente parado para ajustar la frecuencia de las actualizaciones de ubicación etc.
No es necesario tener activa nuestra ubicación para que Google obtenga información sobre nuestra ubicación, se puede obtener información a través de aplicaciones como Google Location Track.
Veamos a continuación las dos formas de acceder a la ubicación de nuestro dispositivos mediante la clase LocationManager o accediendo a los servicios de Google Play.
Falso
El modo más preciso es usando la señal GPS pero consume más energía y no se siempre es posible porque no estamos en un espacio abierto.
Creado con eXeLearning (Ventana nueva)
Se puede indicar una serie de criterios para seleccionar un proveedor de ubicación mediante la clase android.location.Criteria. Los proveedores pueden ordenarse de acuerdo con la precisión, el uso de energía, la capacidad de informar la altitud, la velocidad, el rumbo y el costo monetario. Un ejemplo sería el siguiente código que devuelve el mejor proveedor en base a unos criterios establecidos:
public String getProviderName() {
LocationManager locationManager =(LocationManager)this.getSystemService(Context.LOCATION_SERVICE);
Criteria criteria = new Criteria();
criteria.setPowerRequirement(Criteria.POWER_LOW);
criteria.setAccuracy(Criteria.ACCURACY_FINE);
criteria.setSpeedRequired(true);
criteria.setAltitudeRequired(false);
criteria.setBearingRequired(false);
criteria.setCostAllowed(false);
return locationManager.getBestProvider(criteria, true);
}
Una vez que se ha establecido el mejor proveedor para nuestra aplicación podemos establecer mediante la clase LocationRequest los requerimientos que queremos a la hora de obtener la ubicación entre los que destacamos:
protected void createLocationRequest() {
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setInterval(10000);
locationRequest.setFastestInterval(5000);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
Creado con eXeLearning (Ventana nueva)

Con el tema de la localización se puede solicitar la ubicación en dos situaciones diferentes:
<manifest ... > <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" /> </manifest>
Dependiendo del tipo de proveedor de localización la ubicación en primer plano se necesita declarar los siguientes permisos:
Por ejemplo, se debe declarar el permiso ACCESS_COARSE_LOCATION si nuestra aplicación utiliza únicamente un proveedor de ubicación basado en la red. Otros sistemas como el GPS o la conexión por Bluetooth necesitan una mayor precisión y requieren del permiso ACCESS_FINE_LOCATION.
Al declarar el permiso ACCESS_FINE_LOCATION ya implica la declaración de ACCESS_COARSE_LOCATION
Para trabajar con la API de de Google Maps para Android v2 los permisos ACCESS_COARSE y FINE_LOCATION no son necesarios, no obstante cuando tengamos que utilizar el GPS si tendremos que solicitar los permisos por tanto mostramos el fragmento de código necesario:
private static final int PERMISSIONS_READ_FINE_LOCATION = 100;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (ContextCompat.checkSelfPermission(MainActivity.this,
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED){
if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this,
Manifest.permission.ACCESS_FINE_LOCATION)){
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_READ_FINE_LOCATION);
}else{
ActivityCompat.requestPermissions(MainActivity.this,
new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, PERMISSIONS_READ_FINE_LOCATION);
}
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSIONS_READ_FINE_LOCATION: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show();
}
return;
}
// Otros 'case': para comprobar otros tipos de permisos que se han solicitado
} <br />
} <br />
Este código sigue el flujo de trabajo recomendado por la documentación oficial que puedes consultar en Cómo solicitar permisos de la app.
Hay ciertos aspectos que se deben tener en cuenta respecto al acceso a la ubicación en tiempo de ejecución y en segundo plano te recomendamos que consultes la documentación oficial para profundizar en estos aspectos:
Documentación sobre permisos, ubicación en primer plano y segundo plano.
Creado con eXeLearning (Ventana nueva)
En esta sección veremos cómo obtener la ubicación del dispositivo GPS mediante la clase LocationManager a través de la cual nuestra aplicación puede acceder a los servicios de ubicación en Android y la interfaz LocationListener. Este escuchador tiene una serie de métodos que asociados a los eventos que podemos recibir del proveedor:
TEMPORARILY_UNAVAILABLE, AVAILABLE.A continuación se comprobará si el GPS que será nuestro proveedor de localización o LocationProvider está habilitado. Si el proveedor de ubicación está deshabilitado, puede ofrecer al usuario la oportunidad de habilitarlo en Configuración activando un Intent con la acción ACTION_LOCATION_SOURCE_SETTINGS.

@Override
protected void onStart() {
super.onStart();
// Esta verificación debe realizarse durante onStart () porque el sistema llama
// este método cuando el usuario regresa a la actividad, lo que asegura el deseado
// El proveedor de ubicación se habilita cada vez que la actividad se reanuda desde el estado detenido.
LocationManager locationManager =
(LocationManager) getSystemService(Context.LOCATION_SERVICE);
final boolean gpsEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
if (!gpsEnabled) {
// Cree un diálogo de alerta aquí que solicite que el usuario habilite
// los servicios de ubicación, luego, cuando el usuario hace clic en el botón "Aceptar",
enableLocationSettings();
}
}
private void enableLocationSettings() {
Intent settingsIntent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
startActivity(settingsIntent);
}
A continuación vamos a explicar cómo solicitar la posición:
//Minimo tiempo para updates en Milisegundos
private static final long MIN_TIEMPO_ENTRE_UPDATES = 1000 * 60 * 1; // 1 minuto
//Minima distancia para updates en metros.
private static final float MIN_CAMBIO_DISTANCIA_PARA_UPDATES = 2; // 2 metros
//...
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, <br />MIN_TIEMPO_ENTRE_UPDATES, MIN_CAMBIO_DISTANCIA_PARA_UPDATES, this);
Puedes descargarte el ejemplo completo en el siguiente enlace:
LocationApplication (zip - 17.3 MB).
Creado con eXeLearning (Ventana nueva)
Antes de empezar a utilizar los mapas de Google tenemos que tener unas nociones básicas y las herramientas que necesitamos en nuestro entorno de desarrollo.
Una aplicación que utiliza mapas utiliza la API de Google Maps que se encuentra dentro de los servicios de Google o Google Play Services. Estos servicios están preinstalados en todos los dispositivos móviles de Android y permite a los desarrolladores utilizar las API y servicios de Google como el buscador, el traductor y en nuestro caso poder incorporar mapas a nuestra aplicación. Estos servicios son los que se actualizan a través de Google Play y evolucionan para que nuestro dispositivo no quede desactualizado de esta forma nosotros como desarrolladores podremos utilizar las nuevas funcionalidades de la API de Google Maps en nuestras aplicaciones.
Google ofrece estos servicios a través de Google Cloud Platform (GCP) que es la plataforma en la nube donde Google ha reunido todos sus productos relacionados con la tecnología de la información: Machine Learning, Inteligencia Artificial, Big Data...
Para conectarnos a los servicios de Google es necesario generar una clave de API o (API key) que se utiliza para identificar y autenticar la llamada a una API, en esa petición se debe enviar la API key para validar la llamada. Para ello realizaremos los siguientes pasos:
Si quieres conocer todos los servicios que Google ofrece a las empresas te recomandemos la siguiente lectura:
Qué es Google Cloud y para qué sirve.
Creado con eXeLearning (Ventana nueva)
El objetivo es crear un mapa con un diseño específico que muestre la ubicación actual de nuestro dispositivo. Ya tenemos una API Key y el siguiente paso es habilitar los servicios que ofrece Google en nuestro proyecto. En concreto las API que forman parte de Google Maps Platform se dividen en tres categorías: Maps, Routes y Places y se pueden consultar enListado de API Google Maps disponibles y sus funciones.
Para usar Google Maps Platform hay que habilitar el SDK de Maps para Android como se explica en la sección Cómo habilitar las API de la documentación oficial.
El siguiente paso será añadir a nuestro entorno de trabajo las librerías necesarias para trabajar con la geolocalización, tal como se muestra en la página oficial, se debe añadir la siguiente información a nuestro fichero build.gradle:
apply plugin:'com.android.application'
...
dependencies {
implementation 'com.google.android.gms:play-services-maps:17.0.1'
}
A continuación en el fichero AndroidManifest.xml se deben añadir dos elementos nuevos dentro de <application>. El primero será añadir la versión de Google Play Services utilizada para compilar la aplicación dentro de un elemento <meta-data>:
<meta-data
android:name="com.google.android.gms.version"
android:value="@integer/google_play_services_version" />
}
A continuación, se añade un nuevo elemento <meta-data> donde se debe añadir la API key que se ha generado en el apartado Credenciales en GCP:
<meta-data
android:name="com.google.android.geo.API_KEY"
android:value="YOUR_KEY_HERE" />
Por último, hay que especificar un requisito software que nuestro dispositivo, o concretamente Google Map, hará uso de OpenGL ES versión 2. Para ello hay que usar el elemento <uses-feature> en nuestro fichero manifiesto. Además se añade el atributo required ya que se requiere de este software para que nuestra aplicación funcione:
<uses-feature
android:glEsVersion="0x00020000"
android:required="true"/>
Ya tenemos listo el proyecto para empezar a crear nuesto primer ejemplo con mapas.
Creado con eXeLearning (Ventana nueva)
La clase GoogleMap es la clase que modela un objeto mapa que se ha insertado en nuestra interfaz gráfica mediante un fragmento del tipo MapFragment o SupportMapFragment. Este mapa es similar a los mapas de Google Maps pero hay pequeñas diferencias:
Al igual que todos los objetos en Android esta clase permite una serie de eventos que controlan los gestos del usuario, que responden a la pulsación de teclas y gestos táctiles en el mapa etc. Por ejemplo para manejar las pulsaciones en el mapa se puede implementar la interfaz OnMapClickListener que contiene el método callback onMapClick para el tratamiento del evento. También se puede escuchar los eventos de pulsación larga sobre el mapa con un objeto OnMapLongClickLister. Son muchos los eventos que se pueden gestionar, puedes ver varios ejemplos que ofrece la página oficial muy interesantes en el siguiente enlace Eventos y ejemplos de código.
La actual API de Google Maps para Android requiere que llame a mapFragment.getMapAsync(this). La clase que contiene el fragmento mapa debe implementar la interfaz OnMapReadyCallback para que se ejecute el método OnMapReady, el cual trae un objeto de tipo Google Maps que será utilizado para poder pintar marcadores, mover la cámara,etc.
La visualización del usuario se gestiona mediante una cámara, realmente cuando se quiere actualizar la posición en un mapa no se mueve el mapa sino que se cambia la posición de la cámara mediante el método GoogleMap.moveCamera(CameraUpdate). La clase para manejar una cámara en movimiento es CameraUpdate. Como se pueden crear muchos tipos de cámaras se utiliza el patrón factoría para su creación mediante la clase CameraUpdateFactory al cual se le pasa las nuevas coordenadas y el zoom deseado. Una cámara contiene las siguientes propiedades: objetivo, rumbo, inclinación y zoom. Para saber en qué consiste cada una de ellas y ver ejemplos de código puedes consultar la sección Cámara, vista y ejemplos de código de la documentación oficial.
En la visualización de un mapa existe una serie de controles (zoom, brújula, Mi Ubicación etc) que se pueden activar o desactivar mediante la clase UiSettings que se obtiene mediante el método GoogleMap.getUiSettings. Los cambios realizados en esta clase se reflejan de inmediato en el mapa modificando la apariencia del mapa. Puedes ver un ejemplo que muestra el uso de las opciones y gestos en el enlace Controles y gestos.
Para mostrar la ubicación al usuario se suele utilizar un marcador o clase Marker que puede ser personalizado poniendo un título, cambiando el color o la imagen mediante la clase MarkerOptions. Para añadir información a este marcador lo que se conoce como Ventanas de información se utiliza los métodos title() y snippet(). Para ubicar este marcador se utiliza la clase LatLng que contiene la latitud y longitud como muestra el siguiente ejemplo pero también se puede utilizar la clase Location que contiene más información a parte de la latitud y longitud, como el rumbo, velocidad, una marca de tiempo y otra información relevante.
private final LatLng PERTH = new LatLng(-31.952854, 115.857342);
private Marker markerPerth;<br />// ...
/** Este método se llama cuando el mapa está listo */
@Override
public void onMapReady(GoogleMap map) {
markerPerth = map.addMarker(new MarkerOptions()
.position(PERTH)
.snippet("PETH ubication")
.title("Perth"));
markerPerth.setTag(0);
}
Te mostramos a continuación un ejemplo sencillo para obtener la localización y mostrar varios marcadores en el mapa:
Tutorial para mostrar la ubicación actual y la información de mapa
Otra opción muy interesante dentro de los mapas es poder Dibujar formas puedes ver un ejemplo en el siguiente enlace:
Creado con eXeLearning (Ventana nueva)
Lo primero que vamos a tener es un fragmento dentro de una actividad que ocupará la pantalla completa y mostrará la ubicación de nuestro dispositivo. Este fragment será del tipo google.android.gms.maps.MapFragment, es la forma más sencilla de colocar un mapa en nuestra aplicación:
<fragment <br />android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/map"
tools:context=".MapsActivity"
android:name="com.google.android.gms.maps.SupportMapFragment" />
Vamos a solicitar al servicio de ubicación que actualice su posición usando la clase LocationService que devuelve un objeto de la clase FusedLocationProviderClient.Añadimos la siguiente línea en el método onCreate() de nuestra aplicación:
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this);
Este cliente será el que usaremos para conocer la última localización mediante el método getLastLocation() que devuelve una tarea o Task. Tenemos que controlar si la obtención de la ubicación por parte del cliente es exitosa mediante la interfaz onSuccessListener() o bien si no se ha podido completar la tarea para informar al usuario del error mediante la interfaz onFailureListener():
@SuppressLint("MissingPermission")
public void getLastLocation() {
// Get last known recent location using new Google Play Services SDK (v11+)
fusedLocationClient.getLastLocation()
.addOnSuccessListener(new OnSuccessListener<Location>() {
@Override
public void onSuccess(Location location) {
// GPS location can be null if GPS is switched off
if (location != null) {
onLocationChanged(location);
}
}
})
.addOnFailureListener(new OnFailureListener() {
@Override
public void onFailure(@NonNull Exception e) {
Log.d("MapDemoActivity", "Error trying to get last GPS location");
e.printStackTrace();
}
});
}
Si nuestra aplicación necesita actualizar la ubicación en un período de tiempo establecido, se utiliza el método requestLocationUpdates() al que le pasamos un objeto LocationRequest en la petición y que contiene los parámetros de calidad de servicio para las solicitudes como la precisión y consumo de batería. Por ejemplo, si se quiere una ubicación de alta precisión, se debe crear una solicitud de ubicación con setPriority(int) al valor PRIORITY_HIGH_ACCURACY y setInterval(long) en 10 segundos. Esto sería apropiado para aplicaciones de mapas que muestran su ubicación en tiempo real:
private long UPDATE_INTERVAL = 10 * 1000; /* 10 secs */
private long FASTEST_INTERVAL = 2000; /* 2 sec */
//....
@SuppressLint("MissingPermission")
protected void startLocationUpdates() {
// Create the location request to start receiving updates
LocationRequest locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(UPDATE_INTERVAL);
locationRequest.setFastestInterval(FASTEST_INTERVAL);
// Create LocationSettingsRequest object using location request
LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder();
builder.addLocationRequest(locationRequest);
LocationSettingsRequest locationSettingsRequest = builder.build();
// Check whether location settings are satisfied
SettingsClient settingsClient = LocationServices.getSettingsClient(this);
settingsClient.checkLocationSettings(locationSettingsRequest);
// new Google API SDK v11 uses getFusedLocationProviderClient(this)
fusedLocationClient.requestLocationUpdates(locationRequest, new LocationCallback() {
@Override
public void onLocationResult(LocationResult locationResult) {
// do work here
onLocationChanged(locationResult.getLastLocation());
}
},
Looper.myLooper());
}
Cuando se recibe una nueva localización se debe modificar el mapa moviendo la cámara como ya se ha explicado:
public void onLocationChanged(Location location) {
// New location has now been determined
String msg = "Updated Location: " +
Double.toString(location.getLatitude()) + "," +
Double.toString(location.getLongitude());
Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();
// You can now create a LatLng Object for use with maps
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());
gMap.moveCamera(CameraUpdateFactory.newLatLngZoom(latLng, 16.0f));
}
Antes de habilitar la capa Mi ubicación se debe solicitar el permiso de ubicación del usuario, en el código que mostramos se ha omitido la solicitud:
@SuppressLint("MissingPermission")
@Override
public void onMapReady(GoogleMap googleMap) {
gMap = googleMap;
if (checkPermissions())
googleMap.setMyLocationEnabled(true);
}
Puedes descargarte el ejemplo completo en el siguiente enlace:
Ejemplo SDKMapApplication (zip - 18.32 MB).
Te mostramos a continuación el enlace de la documentación oficial de Android Developer donde puedes ver todos los pasos que se han realizado para crear una aplicación con un mapa:
Creado con eXeLearning (Ventana nueva)
Para incluir el mapa en nuestra aplicación se ha utilizado un objeto SupportMapFragment que proporciona acceso al objeto GoogleMap. Es útil porque se puede utilizar un fragmento en varias actividades. También se puede usar un objeto MapView que representa el mapa en una sección rectangular y hay que llamar desde el ciclo de vida a los diferentes métodos que contiene la clase MapView: onCreate(), onStart(), onResume() etc. Consulta el tutorial Ejemplo del control MapView para ver cómo usarlo en una aplicación. Existen atributos XML que permiten personalizar un mapa desde el diseño, puedes consultar la sección Cómo usar atributos XML de la documentación oficial.
Existen varias representaciones de un mapa y se asignan mediante una llama al método setMapType(), pasando como parámetro el tipo de mapa, a elegir entre los siguientes:
Si se creo un objeto SupportMapFragment o MapView se puede cambiar el estado inicial de un mapa de manera programática si se pasa un objeto GoogleMapOptions:
GoogleMap googleMap = ((SupportMapFragment)getSupportFragmentManager().findFragmentById(R.id.map_fragment)).getMap();
googleMap.setMapType(GoogleMap.MAP_TYPE_SATELLITE);
googleMap.getUiSettings().setMyLocationButtonEnabled(true);
Para cambiar la cámara de un mapa se usa la clase CameraUpdateFactory que contiene métodos para crear objetos CameraUpdate. Puedes consultar el uso de los diferentes métodos en el siguiente tutorial Google Maps Android API.
GoogleMap map = ...;
map.animateCamera(CameraUpdateFactory.zoomIn());
Verdadero
El siguiente tutorial recopila todo lo que hemos trabajado en esta sección de Google Maps explicando los eventos sobre la clase GoogleMap:
Creado con eXeLearning (Ventana nueva)
Mapbox es una plataforma de mapas que ofrece una alternativa a Google Maps para desarrolladores de aplicaciones móviles. En el caso de Android, es uno de tantos SDKs que permite trabajar con los mapas de OpenStreetMap. A pesar de la presencia dominante de Google Maps en el mercado, Mapbox ha ganado popularidad debido a su flexibilidad, personalización y amplias capacidades de desarrollo.
Al integrar Mapbox en aplicaciones Android, los desarrolladores tienen tres principales opciones:
Para poder utilizar algunos (no todos) de los servicios que ofrece Mapbox necesitamos usar unos tokens de acceso que comprueben quienes somos y si tenemos acceso a dichos servicios. Tienes más detalles sobre esto en la entrada correspondiente de la documentación.
Para ello debemos realizar varios pasos.
Desde la dirección de registro podemos crearnos una nueva cuenta. Además de pedirnos algunos datos básicos nos realizará una serie de preguntas dirigidas a conocer qué tipo de desarrollo vamos a hacer, para qué necesitamos usar Mapbox y qué tipo de perfil tenemos.
Una vez creada correctamente la cuenta, ya tendremos generado un token público por defecto que nos permitirá acceder de manera general a las utilidades protegidas de Mapbox.
Por cada aplicación que vayamos a crear y utilice los servicios de Mapbox necesitaremos crear un nuevo token privado de uso exclusivo para dicha app.
Necesitamos establecer el alcance (permisos de acceso a datos y servicios disponibles) que tendrá nuestro proyecto dentro del uso que hagamos de la SDK. Se suele recomendar dejar el alcance público con todo seleccionado y establecer solo el permiso de lectura para descargas en el alcance privado.
Para establecer en nuestro proyecto Android cual es el token privado que hemos establecido para acceder a los servicios de Mapbox y sus APIs utilizaremos Gradle.
MAPBOX_DOWNLOADS_TOKEN=TU_TOKEN_SECRETO_COPIADO
// . . .
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
maven {
url = uri("https://api.mapbox.com/downloads/v2/releases/maven")
// No cambies el usuario de la siguiente línea. Debe ser siempre "mapbox" (no tu usuario).
credentials.username = "mapbox"
// Usa el token secreto almacenado en gradle.properties como la contraseña
credentials.password = providers.gradleProperty("MAPBOX_DOWNLOADS_TOKEN").get()
authentication {
basic(BasicAuthentication)
}
}
}
}
// . . .
<xmp><!--?xml version="1.0" encoding="utf-8"?-->
<resources xmlns:tools="http://schemas.android.com/tools">
<string name="mapbox_access_token" translatable="false" tools:ignore="UnusedResources">TU_TOKEN_PUBLICO_COPIADO</string>
</resources>
</xmp>
Creado con eXeLearning (Ventana nueva)
Para desarrollar usando la SDK para Java de Mapbox hay varios métodos de inclusión en los proyectos. Ya que estamos trabajando con Gradle en éste módulo, usaremos dicho método. Si te fijas en el código del apartado anterior, incluso usando Gradle estaremos accediendo a los repositorios que Mapbox tiene para Maven, que es otra herramienta de inclusión de librerías para Java que quizás conozcas.
Una vez hayas configurado tu proyecto para poder obtener librerías de Mapbox, hay que añadir al build.gradle de nivel de módulo (el que pone Module: app en el modo Android en la vista del Proyecto) las dependencias que necesites usar en tu proyecto.
Es importante que recuerdes que Mapbox requiere que uses la API de Android 14 o una versión superior, por lo que debes revisar la propiedad minSdk de tu proyecto.
Hay 4 paquetes disponibles para usar en tu código java:
GeoJSON: Clases de Java que sirven para manejar el formato GeoJSON para representar datos geoespaciales.
implementation 'com.mapbox.mapboxsdk:mapbox-sdk-geojson:6.15.0'
Services: Wrappers útiles para interactuar con diversas APIs de Mapbox: Matrix, Geocoding, Tilequery, Optimization, Static Image, Map Matching, e Isochrone.
implementation 'com.mapbox.mapboxsdk:mapbox-sdk-services:6.15.0'
Turf: Rutinas para hacer cálculos complejos geoespaciales.
implementation 'com.mapbox.mapboxsdk:mapbox-sdk-turf:6.15.0'
Core: Clases útiles para networking, colores, textos ...
implementation 'com.mapbox.mapboxsdk:mapbox-sdk-core:6.15.0'
Si quieres conocer más sobre cómo funciona esta forma de trabajar sin realmente dejar la responsabilidad de manejar los mapas a Mapbox, puedes explorar la documentación de la librería a través del enlace del apartado anterior.
Android solo permite importar 64 mil métodos. Si tu proyecto se acerca o supera el límite de 64K métodos, puedes mitigar este problema compilando selectivamente solo las API específicas de Mapbox Android Service que estás utilizando en tu proyecto.
Creado con eXeLearning (Ventana nueva)
Maps SDK for Android de Mapbox es una poderosa herramienta que te permite integrar mapas interactivos y personalizables en tus aplicaciones móviles. Aunque hay una gran cantidad de posibilidades que ofrece Mapbox, nos enfocaremos en algunas de las funciones clave que pueden ser útiles para empezar.
Para aprender cómo funciona la librería lo más sencillo es preparar una pequeña aplicación que la use. Vamos a crear una vista que nos muestre la situación actual del usuario dependiendo de lo que le indique el GPS del dispositivo donde funcione dicha app.
De manera similar a como añadíamos JavaSDK a un proyecto, podemos añadir Maps SDK. Simplemente pon la siguiente dependencia en tu build.gradle.
implementation 'com.mapbox.maps:android:10.16.4'
Tenemos que modificar el fichero AndroidManifest.xml con unos nuevos permisos para poder utilizar la localización del dispositivo:
<?xml version="1.0" encoding="utf-8"?>
<manifest . . .>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<application . . .>
<!-- . . . -->
</application>
</manifest>
Ahora es el momento de comenzar ya a trabajar con nuestra aplicación, preparando el layout y el código para que nuestro mapa funcione.
En el layout de la Activity donde queramos que aparezca el mapa, tendremos que añadir el código XML que permite insertar el componente donde se dibujará el mapa. En el código de ejemplo, hemos dejado predefinidas las coordenadas de la ciudad de Aguadulce y un nivel de zoom de 12. En cualquier caso son parámetros que luego desde el código pueden ser modificados e incluso el usuario haciendo uso de la pantalla podrás modificar a tu gusto.
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<com.mapbox.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:mapbox_cameraTargetLat="36.8166"
app:mapbox_cameraTargetLng="-2.5666"
app:mapbox_cameraZoom="12.0" />
</androidx.constraintlayout.widget.ConstraintLayout>
En cuanto al ćodigo, será necesario acceder al objeto MapView del layout para poder ya cargar el mapa y que éste aparezca en pantalla.
public class MainActivity extends AppCompatActivity {
private ActivityMainBinding binding;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView((binding = ActivityMainBinding.inflate(getLayoutInflater())).getRoot());
binding.mapView.getMapboxMap().loadStyleUri(Style.MAPBOX_STREETS);
}
}
En este punto puedes probar a arrancar la app. Verás que aparece un mapa apuntando a la localidad de Aguadulce, tal y como se ha configurado en el componente MapView.
Una vez que ya tenemos lo necesario para visualizar el mapa siguiendo los pasos del punto anterior, vamos a ver cómo empezar a interactuar con él.
Como primer ejemplo veremos cómo seleccionar una ubicación en el mapa haciendo clic en ella y colocando un marker en la posición para representar el lugar escogido. A partir de ahí, podremos obtener las coordenadas de dicha ubicación para almacenarlas o hacer con ellas lo que queramos (que serán los valores de latitud y longitud del punto seleccionado).
Antes de iniciar la implementación de la lógica, necesitamos el icono para usar como marcador. La guía de Mapbox nos recomienda usar uno que alojan en su página web. Impórtalo al ResourceManager (la biblioteca de recursos multimedia que puedes usar en tu app) descargándolo desde aquí:
Para implementar la lógica, necesitaremos los componentes PointAnnotationManager para gestionar los markers que coloquemos en el mapa y GesturesPlugin que nos permite interactuar con el mapa a través de eventos como hacer clic sobre él. Además de esto, necesitaremos lo mínimo que ya sabemos hacer para representar el mapa.
Modifica el código de la app que ya tienes para que la actividad principal refleje lo siguiente:
public class MainActivity extends AppCompatActivity implements Style.OnStyleLoaded, OnMapClickListener {
// . . .
private PointAnnotationManager pointAnnotationManager;
private GesturesPlugin gesturesPlugin;
private Point currentPoint; // Este Point es el de Mapbox no el de Android
@Override
protected void onCreate(Bundle savedInstanceState) {
// . . .
initializeMapView(); // Mueve el código que establece el estilo dentro de un método privado
initializePointAnnotationManager();
initializeGesturesPlugin();
}
@Override
public void onStyleLoaded(@NonNull Style style) {
// Código que se ejecuta cuando se ha cargado el estilo del mapa
}<br /><br /> @Override<br /> public boolean onMapClick(@NonNull Point point) {<br /> pointAnnotationManager.deleteAll();<br /> currentPoint = point;<br /> addMarker(point.latitude(), point.longitude(), getString(R.string.aqui));<br /> return false;<br /> }
private void initializeMapView() {
binding.mapView.getMapboxMap().loadStyleUri(Style.MAPBOX_STREETS, this);
}
private void initializePointAnnotationManager() {
AnnotationPlugin annotationPlugin = AnnotationPluginImplKt.getAnnotations(binding.mapView);
AnnotationConfig annotationConfig = new AnnotationConfig();
pointAnnotationManager =
PointAnnotationManagerKt.createPointAnnotationManager(annotationPlugin, annotationConfig);
}
private void initializeGesturesPlugin() {
gesturesPlugin = GesturesUtils.getGestures(binding.mapView);
gesturesPlugin.addOnMapClickListener(this);
}
private void addMarker(double latitude, double longitude, String title) {
PointAnnotationOptions pointAnnotationOptions = new PointAnnotationOptions()
.withPoint(Point.fromLngLat(longitude, latitude))
.withIconImage(BitmapFactory.decodeResource(getResources(), R.drawable.red_marker))
.withIconOffset(Arrays.asList(0.0, -38.0))
.withTextField(title)
.withTextOffset(Arrays.asList(0.0, -2.0));
pointAnnotationManager.create(pointAnnotationOptions);
}
}
Para visualizar una ubicación en el mapa necesitamos:
En Mapbox lo que se solía conocer como marker ahora se llama Annotation y para colocar uno en un mapa de Mapbox hay que seguir el código que se ejecuta en el método addMarker(latitude, longitude, title). Hay que tener en cuenta que el código tendrá que ejecutarse tras cargarse el mapa. Es por eso que lo hacemos invocándolo desde el método onStyleLoaded, que a su vez será ejecutado una vez termine de cargarse el mapa.
Hay que tener en cuenta que, antes de poder añadir markers, tendremos que inicializar el PointAnnotationManager ejecutando el método initializePointAnnotationManager().
Modifica el código de tu app para que quede parecido a lo siguiente:
public class MainActivity extends AppCompatActivity implements Style.OnStyleLoaded {
private ActivityMainBinding binding;
private PointAnnotationManager pointAnnotationManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
// . . .
initializeMapView();
initializePointAnnotationManager();
}
@Override
public void onStyleLoaded(@NotNull Style style) {
addMarker(36.809647148813326, -2.58291798769084, "IES Aguadulce");
}
private void initializeMapView() {
// . . .
}
private void initializePointAnnotationManager() {
// . . .
}
private void addMarker(double latitude, double longitude, String title) {
// . . .
}
}
Y si además queremos posicionar directamente la cámara del mapa en esa posición y, opcionalmente, acercarla y modificar otros parámetros, podemos hacerlo como sigue, añadiendo el código dentro del método onStyleLoaded justo después de invocar a addMarker, tal y como hemos hecho para añadir el marker.
En este caso metemos el código en un método para que podamos usarlo siempre que queramos posicionar sobre un punto determinado con una latitud y longitud dadas. También se podrían parametrizar otros valores como el nivel de zoom para poder personalizarlo en cada llamada.
private void setCameraPosition(double latitude, double longitude) {
CameraOptions cameraPosition = new CameraOptions.Builder()
.center(Point.fromLngLat(longitude, latitude))
.pitch(45.0)
.zoom(16.0)
.bearing(-17.6)
.build();
binding.mapView.getMapboxMap().setCamera(cameraPosition);
}
Llama al método para colocar la cámara en la misma posición donde hemos colocado un marcador con addMarker y prueba la app.
En el siguiente ejemplo se muestra como, utilizando el GPS, podemos acceder a la ubicación actual del usuario. En este caso se calcula su ubicación y se moviliza la cámara del mapa para posicionarnos en ella. Hay que tener en cuenta que puesto en los ejemplos anteriores ya hemos añadido los permisos necesarios para trabajar con el GPS no lo tendremos que hacer ahora. En caso de que no se haya hecho habrá que añadir los permisos en el AndroidManifest.xml. Si estas utilizando un emulador de Android, deberás configurar su localización en caso de que no quieras que utilice California como su localización por defecto.
Lo primero será crear un botón flotante (FloatingActionButton) en el layout del mapa de forma que el usuario pueda pulsarlo cuando quiera conocer y acceder a su posición en el mapa. Sitúalo en la actividad principal.
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/bt_ubicacion"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="16dp"
tools:ignore="VectorDrawableCompat"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
A continuación, en la Activity del mapa tendremos que añadir el código necesario para hacer funcionar el GPS y acceder a la posición del usuario:
// . . .
import android.Manifest;
public class MainActivity extends AppCompatActivity
implements /* . . . */ LocationEngineCallback<LocationEngineResult><locationengineresult> {
// . . .
@Override
protected void onCreate(Bundle savedInstanceState) {
// . . .
initializeLocationEngine();
binding.btUbicacion.setOnClickListener(v -> {
if (initialPoint != null) {
pointAnnotationManager.deleteAll();
setCameraPosition(initialPoint);
addMarker(initialPoint, getString(R.string.aqui));
}
});
}
// . . .
@Override
public void onSuccess(LocationEngineResult locationEngineResult) {
Location currentLocation = locationEngineResult.getLastLocation();
Point point = Point.fromLngLat(currentLocation.getLongitude(), currentLocation.getLatitude());
initialPoint = point;
setCameraPosition(point);
addMarker(point, getString(R.string.aqui));
}
@Override
public void onFailure(@NonNull Exception e) {
Log.e("LocationEngineCallback", e.getMessage());
}
private void initializeLocationEngine() {
LocationEngine locationEngine = LocationEngineProvider.getBestLocationEngine(this);
if (ActivityCompat.checkSelfPermission(this, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(this,
ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{ACCESS_FINE_LOCATION, ACCESS_COARSE_LOCATION}, 1);
return;
}
locationEngine.getLastLocation(this);
}
private void setCameraPosition(Point point) {
CameraOptions cameraPosition = new CameraOptions.Builder()
.center(point)
.pitch(0.0)
.zoom(13.5)
.bearing(-17.6)
.build();
binding.mapView.getMapboxMap().setCamera(cameraPosition);
}
}</locationengineresult>
Creado con eXeLearning (Ventana nueva)
El SDK de Navegación para Android te permite construir una experiencia de navegación completa con la potencia de la API de Direcciones de Mapbox. Puedes crear una interfaz de usuario de navegación paso a paso dentro de tu aplicación utilizando nuestros componentes preconstruidos o utilizar la lógica central directamente para construir algo verdaderamente personalizado.
El SDK de Navegación proporciona una colección de funciones que son críticas al construir proyectos de navegación, incluyendo:
El SDK de Navegación ofrece dos tipos de navegación: navegación paso a paso y modo de conducción libre.
La navegación paso a paso, también conocida como guía activa, es probablemente lo que viene a la mente cuando piensas en una experiencia típica de navegación paso a paso: el usuario recibe instrucciones paso a paso a medida que avanza a lo largo de la ruta.
La navegación en modo de conducción libre, también conocida como navegación pasiva, es una característica única del SDK de Navegación de Mapbox que permite a los conductores aprovechar algunas funciones de navegación sin seleccionar una ruta y seguir instrucciones paso a paso hacia un destino establecido.
El SDK de Navegación de Mapbox para Android es compatible con aplicaciones que:
Utilizando los servicios de Android de Mapbox se pueden calcular las rutas y distancias entre dos puntos dados sobre el mapa, para posteriormente pintarla.
Aprovechando la aplicación del ejercicio resuelto del apartado anterior, puedes hacer algunos cambios para añadir el dibujado de rutas.

Para ello, lo primero que tenemos que hacer es añadir la correspondiente librería en el build.gradle de nuestra app, tal y como ya hicimos con la API de Android antes de empezar a trabajar con la librería Mapbox. En este caso se trata de acceder a la API específica para el cálculo de rutas. En total, tendríamos que añadir las siguientes librerías Mapbox:
dependencies {
// . . .
implementation 'com.mapbox.navigation:core:2.11.0'
implementation 'com.mapbox.navigation:android:2.11.0'
// . . .
}
Y a continuación, dos métodos, calculateRoute(Point origin, Point destination) para hacer el cálculo de la ruta entre dos puntos dados, utilizando como punto de partida dos objetos Point que identifican la longitud y la latitud de los mismos. Con este método prepararemos la ruta que queremos calcular e invocaremos a directions.enqueeCall(this) para calcular, en segundo plano, la ruta entre los puntos datos.
Hemos implementado la interface Callback<DirectionsResponse> en nuestra Activity por lo que, cuando la ruta esté lista, se invocará al método onResponse automáticamente para dibujarla en el mapa.
public class MainActivity extends AppCompatActivity
implements /* . . . */ Callback<DirectionsResponse> { // Este Callback es de la librería Retrofit (viene dentro de Navigation SDK)
// . . .
@Override
protected void onCreate(Bundle savedInstanceState) {
// . . .
calculateRoute(Point.fromLngLat(-2.5666, 36.8166), Point.fromLngLat(-2.4597, 36.8381));
}
@Override
public void onResponse(Call<directionsresponse> call, Response<directionsresponse> response) {
DirectionsRoute mainRoute = response.body().routes().get(0);
Log.d("ROUTELEGS", String.valueOf(mainRoute.legs().size()));
for (RouteLeg routeLeg : mainRoute.legs()) {
Log.d("LEGS", String.valueOf(routeLeg.steps().size()));
for (LegStep legStep : routeLeg.steps()) {
Log.d("STEP", legStep.name() + " " + legStep.speedLimitSign() + " " + legStep);
}
}
binding.mapView.getMapboxMap().getStyle(style -> {
LineString routeLine = LineString.fromPolyline(mainRoute.geometry(), PRECISION_6);
GeoJsonSource routeSource = new GeoJsonSource.Builder("trace-source")
.geometry(routeLine)
.build();
LineLayer routeLayer = new LineLayer("trace-layer", "trace-source")
.lineWidth(7.f)
.lineColor(Color.BLUE)
.lineOpacity(1f);
SourceUtils.addSource(style, routeSource);
LayerUtils.addLayer(style, routeLayer);
});
}
@Override
public void onFailure(Call<DirectionsResponse> call, Throwable t) {
Log.e("MainActivity", "Fallo al invocar a la API de Mapbox", t);
}
// . . .
// Calcula la ruta entre dos puntos dados
private void calculateRoute(Point origin, Point destination) {
RouteOptions routeOptions =
RouteOptions.builder().baseUrl(Constants.BASE_API_URL)
.user(Constants.MAPBOX_USER)
.profile(DirectionsCriteria.PROFILE_WALKING)
.steps(true)
.coordinatesList(Arrays.asList(origin, destination)).build();
MapboxDirections directions = MapboxDirections.builder().routeOptions(routeOptions)
.accessToken(getString(R.string.mapbox_access_token)).build();
directions.enqueueCall(this);
}
}</directionsresponse></directionsresponse>
Creado con eXeLearning (Ventana nueva)
Materiales desarrollados inicialmente por el Ministerio de Educación, Cultura y Deporte y actualizados por el profesorado de la Junta de Andalucía bajo licencia Creative Commons BY-NC-SA.

Antes de cualquier uso leer detenidamente el siguente Aviso legal
| Versión: 02.01.00 | Fecha de actualización: 08/04/24 | Autoría: José Ángel González Molina |
|---|---|---|
Ubicación: 2.7 | ||
| Versión: 02.00.00 | Fecha de actualización: 09/06/21 | Autoría: Lourdes Rodríguez Morón |
|---|---|---|
Ubicación: Todo el documento Ubicación: Unidad 6 Ubicación: Todo el documento Ubicación: Todo el documento | ||
| Versión: 01.00.00 | Fecha de actualización: 08/06/21 | |
|---|---|---|
| Versión inicial de los materiales. | ||