Un gesto táctil se produce cuando un usuario coloca uno o más dedos en la pantalla táctil, y tu app interpreta este patrón de toques como un gesto. La detección de gestos consta de dos fases:
- Recopilación de datos de eventos táctiles
- Interpreta los datos para determinar si cumplen con los criterios de los gestos que admite tu app.
Clases de AndroidX
En los ejemplos de este documento, se usan las clases GestureDetectorCompat
y MotionEventCompat
. Estas clases se encuentran en la biblioteca de AndroidX. Usa clases de AndroidX siempre que sea posible para proporcionar compatibilidad con dispositivos anteriores.
MotionEventCompat
no reemplaza la clase MotionEvent
. En su lugar, proporciona métodos de utilidades estáticos que puedes pasar al objeto MotionEvent
para recibir la acción asociada con ese evento.
Cómo recopilar datos
Cuando un usuario coloca uno o más dedos en la pantalla, se activa la devolución de llamada onTouchEvent()
en la vista que recibe los eventos táctiles. Por cada secuencia de eventos táctiles (como posición, presión, tamaño y adición de otro dedo) que se identifica como gesto, onTouchEvent()
se activa varias veces.
El gesto comienza cuando el usuario toca la pantalla por primera vez, continúa mientras el sistema rastrea la posición del dedo o los dedos del usuario y finaliza cuando captura el evento final del último dedo del usuario cuando sale de la pantalla.
A lo largo de esta interacción, el MotionEvent
entregado a onTouchEvent()
proporciona los detalles de cada interacción. Tu app puede usar los datos que proporciona MotionEvent
para determinar si ocurre un gesto importante.
Cómo capturar eventos táctiles para objetos Activity o View
Para interceptar eventos táctiles en una Activity
o View
, anula la devolución de llamada onTouchEvent()
.
En el siguiente fragmento de código, se usa getAction()
para extraer la acción que realiza el usuario del parámetro event
.
De esta manera, obtendrás los datos sin procesar que necesitas para determinar si se produce un gesto que te interesa.
Kotlin
class MainActivity : Activity() { ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. override fun onTouchEvent(event: MotionEvent): Boolean { return when (event.action) { MotionEvent.ACTION_DOWN -> { Log.d(DEBUG_TAG, "Action was DOWN") true } MotionEvent.ACTION_MOVE -> { Log.d(DEBUG_TAG, "Action was MOVE") true } MotionEvent.ACTION_UP -> { Log.d(DEBUG_TAG, "Action was UP") true } MotionEvent.ACTION_CANCEL -> { Log.d(DEBUG_TAG, "Action was CANCEL") true } MotionEvent.ACTION_OUTSIDE -> { Log.d(DEBUG_TAG, "Movement occurred outside bounds of current screen element") true } else -> super.onTouchEvent(event) } } }
Java
public class MainActivity extends Activity { ... // This example shows an Activity. You can use the same approach if you are // subclassing a View. @Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()) { case (MotionEvent.ACTION_DOWN) : Log.d(DEBUG_TAG,"Action was DOWN"); return true; case (MotionEvent.ACTION_MOVE) : Log.d(DEBUG_TAG,"Action was MOVE"); return true; case (MotionEvent.ACTION_UP) : Log.d(DEBUG_TAG,"Action was UP"); return true; case (MotionEvent.ACTION_CANCEL) : Log.d(DEBUG_TAG,"Action was CANCEL"); return true; case (MotionEvent.ACTION_OUTSIDE) : Log.d(DEBUG_TAG,"Movement occurred outside bounds of current screen element"); return true; default : return super.onTouchEvent(event); } }
Este código produce mensajes como los siguientes en Logcat a medida que el usuario presiona, mantiene presionado y arrastra:
GESTURES D Action was DOWN GESTURES D Action was UP GESTURES D Action was MOVE
En el caso de los gestos personalizados, puedes realizar tu propio procesamiento en estos eventos para determinar si representan un gesto que necesitas controlar. Sin embargo, si tu app usa gestos comunes, como presionar dos veces, mantener presionado, arrastrar y soltar, entre otros, puedes aprovechar la clase GestureDetector
. GestureDetector
facilita la detección de gestos comunes sin procesar los eventos táctiles individuales por tu cuenta. Esto se analiza con más detalle en Cómo detectar gestos.
Cómo capturar eventos táctiles para una sola vista
Como alternativa a onTouchEvent()
, puedes adjuntar un objeto View.OnTouchListener
a cualquier objeto View
con el método setOnTouchListener()
. Esto permite escuchar eventos táctiles sin subclasificar un View
existente, como se muestra en el siguiente ejemplo:
Kotlin
findViewById<View>(R.id.my_view).setOnTouchListener { v, event -> // Respond to touch events. true }
Java
View myView = findViewById(R.id.my_view); myView.setOnTouchListener(new OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { // Respond to touch events. return true; } });
Ten cuidado de no crear un objeto de escucha que muestre false
para el evento ACTION_DOWN
.
Si lo haces, no se llama al objeto de escucha para la secuencia de eventos ACTION_MOVE
y ACTION_UP
posterior. Esto se debe a que ACTION_DOWN
es el punto de partida para todos los eventos táctiles.
Si creas una vista personalizada, puedes anular onTouchEvent()
, como se describió antes.
Cómo detectar gestos
Android proporciona la clase GestureDetector
para detectar gestos comunes. Entre los gestos que admite, se incluyen onDown()
, onLongPress()
y onFling()
.
Puedes usar GestureDetector
junto con el método onTouchEvent()
que se describió antes.
Cómo detectar todos los gestos compatibles
Cuando creas una instancia de un objeto GestureDetectorCompat
, uno de los parámetros que toma es una clase que implementa la interfaz GestureDetector.OnGestureListener
. GestureDetector.OnGestureListener
notifica a los usuarios cuando se produce un evento táctil en particular. Para que el objeto GestureDetector
pueda recibir eventos, anula el método onTouchEvent()
de la vista o la actividad y pasa todos los eventos observados a la instancia del detector.
En el siguiente fragmento, un valor que se muestra de true
de los métodos on<TouchEvent>
individuales indica que se gestionó el evento táctil. Un valor de muestra de false
pasa eventos a través de la pila de vistas hasta que el toque se controla correctamente.
Si ejecutas el siguiente fragmento en una app de prueba, podrás tener una idea de cómo se activan las acciones cuando interactúas con la pantalla táctil y cuál es el contenido de MotionEvent
para cada evento táctil. Luego, puedes ver cuántos datos se generan para interacciones simples.
Kotlin
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity(), GestureDetector.OnGestureListener, GestureDetector.OnDoubleTapListener { private lateinit var mDetector: GestureDetectorCompat // Called when the activity is first created. public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Instantiate the gesture detector with the // application context and an implementation of // GestureDetector.OnGestureListener. mDetector = GestureDetectorCompat(this, this) // Set the gesture detector as the double-tap // listener. mDetector.setOnDoubleTapListener(this) } override fun onTouchEvent(event: MotionEvent): Boolean { return if (mDetector.onTouchEvent(event)) { true } else { super.onTouchEvent(event) } } override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } override fun onLongPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onLongPress: $event") } override fun onScroll( event1: MotionEvent, event2: MotionEvent, distanceX: Float, distanceY: Float ): Boolean { Log.d(DEBUG_TAG, "onScroll: $event1 $event2") return true } override fun onShowPress(event: MotionEvent) { Log.d(DEBUG_TAG, "onShowPress: $event") } override fun onSingleTapUp(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapUp: $event") return true } override fun onDoubleTap(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTap: $event") return true } override fun onDoubleTapEvent(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDoubleTapEvent: $event") return true } override fun onSingleTapConfirmed(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onSingleTapConfirmed: $event") return true } }
Java
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; } }
Cómo detectar un subconjunto de gestos compatibles
Si solo deseas procesar algunos gestos, puedes extender GestureDetector.SimpleOnGestureListener
en lugar de implementar la interfaz GestureDetector.OnGestureListener
.
GestureDetector.SimpleOnGestureListener
proporciona una implementación para todos los métodos on<TouchEvent>
y muestra false
para todos ellos. Esto te permite anular solo los métodos que te interesan. Por ejemplo, en el siguiente fragmento de código, se crea una clase que extiende GestureDetector.SimpleOnGestureListener
y anula onFling()
y onDown()
.
Ya sea que uses GestureDetector.OnGestureListener
o GestureDetector.SimpleOnGestureListener
, se recomienda implementar un método onDown()
que muestre true
. Esto se debe a que todos los gestos comienzan con un mensaje onDown()
. Si muestras false
desde onDown()
, como lo hace GestureDetector.SimpleOnGestureListener
de forma predeterminada, el sistema supone que deseas ignorar el resto del gesto y no se llama a los otros métodos de GestureDetector.OnGestureListener
. Esto podría causar problemas inesperados en tu app. Solo muestra false
desde onDown()
si realmente deseas ignorar un gesto completo.
Kotlin
private const val DEBUG_TAG = "Gestures" class MainActivity : Activity() { private lateinit var mDetector: GestureDetectorCompat public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mDetector = GestureDetectorCompat(this, MyGestureListener()) } override fun onTouchEvent(event: MotionEvent): Boolean { mDetector.onTouchEvent(event) return super.onTouchEvent(event) } private class MyGestureListener : GestureDetector.SimpleOnGestureListener() { override fun onDown(event: MotionEvent): Boolean { Log.d(DEBUG_TAG, "onDown: $event") return true } override fun onFling( event1: MotionEvent, event2: MotionEvent, velocityX: Float, velocityY: Float ): Boolean { Log.d(DEBUG_TAG, "onFling: $event1 $event2") return true } } }
Java
public class MainActivity extends Activity { private GestureDetectorCompat mDetector; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mDetector = new GestureDetectorCompat(this, new MyGestureListener()); } @Override public boolean onTouchEvent(MotionEvent event){ if (this.mDetector.onTouchEvent(event)) { return true; } return super.onTouchEvent(event); } class MyGestureListener extends GestureDetector.SimpleOnGestureListener { private static final String DEBUG_TAG = "Gestures"; @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; } } }
Recursos adicionales
- Descripción general de eventos de entrada
- Descripción general de los sensores
- Cómo crear una vista personalizada interactiva