Android: Cómo manejar los gestos de deslizamiento de derecha a izquierda

Quiero que mi aplicación reconozca cuando un usuario pasa de derecha a izquierda en la pantalla del teléfono.

¿Como hacer esto?

OnSwipeTouchListener.java :

import android.content.Context; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; public class OnSwipeTouchListener implements OnTouchListener { private final GestureDetector gestureDetector; public OnSwipeTouchListener (Context ctx){ gestureDetector = new GestureDetector(ctx, new GestureListener()); } @Override public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } private final class GestureListener extends SimpleOnGestureListener { private static final int SWIPE_THRESHOLD = 100; private static final int SWIPE_VELOCITY_THRESHOLD = 100; @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { boolean result = false; try { float diffY = e2.getY() - e1.getY(); float diffX = e2.getX() - e1.getX(); if (Math.abs(diffX) > Math.abs(diffY)) { if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { if (diffX > 0) { onSwipeRight(); } else { onSwipeLeft(); } result = true; } } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { if (diffY > 0) { onSwipeBottom(); } else { onSwipeTop(); } result = true; } } catch (Exception exception) { exception.printStackTrace(); } return result; } } public void onSwipeRight() { } public void onSwipeLeft() { } public void onSwipeTop() { } public void onSwipeBottom() { } } 

Uso:

 imageView.setOnTouchListener(new OnSwipeTouchListener(MyActivity.this) { public void onSwipeTop() { Toast.makeText(MyActivity.this, "top", Toast.LENGTH_SHORT).show(); } public void onSwipeRight() { Toast.makeText(MyActivity.this, "right", Toast.LENGTH_SHORT).show(); } public void onSwipeLeft() { Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show(); } public void onSwipeBottom() { Toast.makeText(MyActivity.this, "bottom", Toast.LENGTH_SHORT).show(); } }); 

Este código detecta los swipes izquierdo y derecho, evita llamadas de API obsoletas y tiene otras mejoras diversas sobre respuestas anteriores.

 /** * Detects left and right swipes across a view. */ public class OnSwipeTouchListener implements OnTouchListener { private final GestureDetector gestureDetector; public OnSwipeTouchListener(Context context) { gestureDetector = new GestureDetector(context, new GestureListener()); } public void onSwipeLeft() { } public void onSwipeRight() { } public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } private final class GestureListener extends SimpleOnGestureListener { private static final int SWIPE_DISTANCE_THRESHOLD = 100; private static final int SWIPE_VELOCITY_THRESHOLD = 100; @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { float distanceX = e2.getX() - e1.getX(); float distanceY = e2.getY() - e1.getY(); if (Math.abs(distanceX) > Math.abs(distanceY) && Math.abs(distanceX) > SWIPE_DISTANCE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { if (distanceX > 0) onSwipeRight(); else onSwipeLeft(); return true; } return false; } } } 

Utilícelo de esta manera:

 view.setOnTouchListener(new OnSwipeTouchListener(context) { @Override public void onSwipeLeft() { // Whatever } }); 

Si también necesita procesar eventos de clic aquí algunas modificaciones:

 public class OnSwipeTouchListener implements OnTouchListener { private final GestureDetector gestureDetector = new GestureDetector(new GestureListener()); public boolean onTouch(final View v, final MotionEvent event) { return gestureDetector.onTouchEvent(event); } private final class GestureListener extends SimpleOnGestureListener { private static final int SWIPE_THRESHOLD = 100; private static final int SWIPE_VELOCITY_THRESHOLD = 100; @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { boolean result = false; try { float diffY = e2.getY() - e1.getY(); float diffX = e2.getX() - e1.getX(); if (Math.abs(diffX) > Math.abs(diffY)) { if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { if (diffX > 0) { result = onSwipeRight(); } else { result = onSwipeLeft(); } } } else { if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { if (diffY > 0) { result = onSwipeBottom(); } else { result = onSwipeTop(); } } } } catch (Exception exception) { exception.printStackTrace(); } return result; } } public boolean onSwipeRight() { return false; } public boolean onSwipeLeft() { return false; } public boolean onSwipeTop() { return false; } public boolean onSwipeBottom() { return false; } } 

Y el uso de muestra:

  background.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View arg0) { toggleSomething(); } }); background.setOnTouchListener(new OnSwipeTouchListener() { public boolean onSwipeTop() { Toast.makeText(MainActivity.this, "top", Toast.LENGTH_SHORT).show(); return true; } public boolean onSwipeRight() { Toast.makeText(MainActivity.this, "right", Toast.LENGTH_SHORT).show(); return true; } public boolean onSwipeLeft() { Toast.makeText(MainActivity.this, "left", Toast.LENGTH_SHORT).show(); return true; } public boolean onSwipeBottom() { Toast.makeText(MainActivity.this, "bottom", Toast.LENGTH_SHORT).show(); return true; } }); 

Ampliación de la respuesta de Mirek, para el caso de que desee utilizar los gestos de desplazamiento dentro de una vista de desplazamiento. De forma predeterminada, el oyente táctil de la vista de desplazamiento se deshabilita y, por lo tanto, la acción de desplazamiento no ocurre. Para solucionar esto, necesitas anular el método dispatchTouchEvent de la Activity y devolver la versión heredada de este método una vez que hayas terminado con tu propio listener.

Para hacer algunas modificaciones al código de Mirek: agrego un getter para el gestureDetector en el OnSwipeTouchListener .

 public GestureDetector getGestureDetector(){ return gestureDetector; } 

Declare OnSwipeTouchListener dentro de la Actividad como un campo de toda la clase.

 OnSwipeTouchListener onSwipeTouchListener; 

Modifique el código de uso en consecuencia:

 onSwipeTouchListener = new OnSwipeTouchListener(MyActivity.this) { public void onSwipeTop() { Toast.makeText(MyActivity.this, "top", Toast.LENGTH_SHORT).show(); } public void onSwipeRight() { Toast.makeText(MyActivity.this, "right", Toast.LENGTH_SHORT).show(); } public void onSwipeLeft() { Toast.makeText(MyActivity.this, "left", Toast.LENGTH_SHORT).show(); } public void onSwipeBottom() { Toast.makeText(MyActivity.this, "bottom", Toast.LENGTH_SHORT).show(); } }); imageView.setOnTouchListener(onSwipeTouchListener); 

Y reemplaza el método dispatchTouchEvent dentro de Activity :

 @Override public boolean dispatchTouchEvent(MotionEvent ev){ swipeListener.getGestureDetector().onTouchEvent(ev); return super.dispatchTouchEvent(ev); } 

Ahora, tanto las acciones de desplazamiento como las de desplazamiento deben funcionar.

Con el fin de tener Click Listener , DoubleClick Listener , OnLongPress Listener DoubleClick Listener , OnLongPress Listener Swipe Left , Swipe Right , Swipe Up , Swipe Down en la View individual que necesita setOnTouchListener . es decir,

 view.setOnTouchListener(new OnSwipeTouchListener(MainActivity.this) { @Override public void onClick() { super.onClick(); // your on click here } @Override public void onDoubleClick() { super.onDoubleClick(); // your on onDoubleClick here } @Override public void onLongClick() { super.onLongClick(); // your on onLongClick here } @Override public void onSwipeUp() { super.onSwipeUp(); // your swipe up here } @Override public void onSwipeDown() { super.onSwipeDown(); // your swipe down here. } @Override public void onSwipeLeft() { super.onSwipeLeft(); // your swipe left here. } @Override public void onSwipeRight() { super.onSwipeRight(); // your swipe right here. } }); } 

Para esto se necesita la clase OnSwipeTouchListener que implementa OnTouchListener .

 public class OnSwipeTouchListener implements View.OnTouchListener { private GestureDetector gestureDetector; public OnSwipeTouchListener(Context c) { gestureDetector = new GestureDetector(c, new GestureListener()); } public boolean onTouch(final View view, final MotionEvent motionEvent) { return gestureDetector.onTouchEvent(motionEvent); } private final class GestureListener extends GestureDetector.SimpleOnGestureListener { private static final int SWIPE_THRESHOLD = 100; private static final int SWIPE_VELOCITY_THRESHOLD = 100; @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onSingleTapUp(MotionEvent e) { onClick(); return super.onSingleTapUp(e); } @Override public boolean onDoubleTap(MotionEvent e) { onDoubleClick(); return super.onDoubleTap(e); } @Override public void onLongPress(MotionEvent e) { onLongClick(); super.onLongPress(e); } // Determines the fling velocity and then fires the appropriate swipe event accordingly @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { boolean result = false; try { float diffY = e2.getY() - e1.getY(); float diffX = e2.getX() - e1.getX(); if (Math.abs(diffX) > Math.abs(diffY)) { if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { if (diffX > 0) { onSwipeRight(); } else { onSwipeLeft(); } } } else { if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { if (diffY > 0) { onSwipeDown(); } else { onSwipeUp(); } } } } catch (Exception exception) { exception.printStackTrace(); } return result; } } public void onSwipeRight() { } public void onSwipeLeft() { } public void onSwipeUp() { } public void onSwipeDown() { } public void onClick() { } public void onDoubleClick() { } public void onLongClick() { } } 

Utilice SwipeListView y déjelo manejar la detección de gestos para usted.

Captura de pantalla

Para agregar un onClick también, esto es lo que hice.

 .... // in OnSwipeTouchListener class private final class GestureListener extends SimpleOnGestureListener { .... // normal GestureListener code @Override public boolean onSingleTapConfirmed(MotionEvent e) { onClick(); // my method return super.onSingleTapConfirmed(e); } } // end GestureListener class public void onSwipeRight() { } public void onSwipeLeft() { } public void onSwipeTop() { } public void onSwipeBottom() { } public void onClick(){ } // as normal @Override public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } } // end OnSwipeTouchListener class 

Estoy utilizando fragmentos, por lo que el uso de getActivity () para el contexto. Así es como lo implementé, y funciona.


 myLayout.setOnTouchListener(new OnSwipeTouchListener(getActivity()) { public void onSwipeTop() { Toast.makeText(getActivity(), "top", Toast.LENGTH_SHORT).show(); } public void onSwipeRight() { Toast.makeText(getActivity(), "right", Toast.LENGTH_SHORT).show(); } public void onSwipeLeft() { Toast.makeText(getActivity(), "left", Toast.LENGTH_SHORT).show(); } public void onSwipeBottom() { Toast.makeText(getActivity(), "bottom", Toast.LENGTH_SHORT).show(); } public void onClick(){ Toast.makeText(getActivity(), "clicked", Toast.LENGTH_SHORT).show(); } }); 

Método @Edward Brey funciona muy bien. Si alguien también desea copiar y pegar las importaciones para OnSwipeTouchListener , aquí están:

  import android.content.Context; import android.view.GestureDetector; import android.view.GestureDetector.SimpleOnGestureListener; import android.view.MotionEvent; import android.view.View; import android.view.View.OnTouchListener; 

Necesita algunas actualizaciones menores. Las variables no coinciden en el método onTouch y la clase de excepción no se importa. Tendría más sentido simplemente devolver false de onFling (), en lugar de iniciar una variable, asignarle un valor, no hacer nada con él, y simplemente devolverlo.

Algunas partes del método onTouch son incorrectas. See view / motionEvent no se puede resolver con un swipe variable

Una sugerencia útil que me hubiera ahorrado un poco de tiempo y espero que pueda ayudar a otros: cuando se utiliza este método, sólo desea agregar la etiqueta "implementa" a su clase OnSwipeTouchListener. Su Actividad y Vista no lo están implementando. Sólo están aprovechando su clase que ya lo hace!

+1 para Mirek porque su código todavía me dio lo que necesitaba para la mía 🙂

Una pequeña modificación de @Mirek Rusin respuesta y ahora se puede detectar swipes multitouch. Este código está en Kotlin:

 class OnSwipeTouchListener(ctx: Context, val onGesture: (gestureCode: Int) -> Unit) : OnTouchListener { private val SWIPE_THRESHOLD = 200 private val SWIPE_VELOCITY_THRESHOLD = 200 private val gestureDetector: GestureDetector var fingersCount = 0 fun resetFingers() { fingersCount = 0 } init { gestureDetector = GestureDetector(ctx, GestureListener()) } override fun onTouch(v: View, event: MotionEvent): Boolean { if (event.pointerCount > fingersCount) { fingersCount = event.pointerCount } return gestureDetector.onTouchEvent(event) } private inner class GestureListener : SimpleOnGestureListener() { override fun onDown(e: MotionEvent): Boolean { return true } override fun onFling(e1: MotionEvent, e2: MotionEvent, velocityX: Float, velocityY: Float): Boolean { var result = false try { val diffY = e2.y - e1.y val diffX = e2.x - e1.x if (Math.abs(diffX) > Math.abs(diffY)) { if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { if (diffX > 0) { val gesture = when (fingersCount) { 1 -> Gesture.SWIPE_RIGHT 2 -> Gesture.TWO_FINGER_SWIPE_RIGHT 3 -> Gesture.THREE_FINGER_SWIPE_RIGHT else -> -1 } if (gesture > 0) { onGesture.invoke(gesture) } } else { val gesture = when (fingersCount) { 1 -> Gesture.SWIPE_LEFT 2 -> Gesture.TWO_FINGER_SWIPE_LEFT 3 -> Gesture.THREE_FINGER_SWIPE_LEFT else -> -1 } if (gesture > 0) { onGesture.invoke(gesture) } } resetFingers() } } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { if (diffY > 0) { val gesture = when (fingersCount) { 1 -> Gesture.SWIPE_DOWN 2 -> Gesture.TWO_FINGER_SWIPE_DOWN 3 -> Gesture.THREE_FINGER_SWIPE_DOWN else -> -1 } if (gesture > 0) { onGesture.invoke(gesture) } } else { val gesture = when (fingersCount) { 1 -> Gesture.SWIPE_UP 2 -> Gesture.TWO_FINGER_SWIPE_UP 3 -> Gesture.THREE_FINGER_SWIPE_UP else -> -1 } if (gesture > 0) { onGesture.invoke(gesture) } } resetFingers() } result = true } catch (exception: Exception) { exception.printStackTrace() } return result } }} 

Donde Gesture.SWIPE_RIGHT y otros son únicos entero indentificator de gesto que estoy usando para detectar tipo de gesto más adelante en mi actividad:

 rootView?.setOnTouchListener(OnSwipeTouchListener(this, { gesture -> log(Gesture.parseName(this, gesture)) })) 

Así que usted ve gesto aquí es una variable entera que contiene el valor que he pasado antes.

@Mirek Rusin answeir es muy bueno. Pero, hay pequeño error, y el arreglo se requried –

 public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { boolean result = false; try { float diffY = e2.getY() - e1.getY(); float diffX = e2.getX() - e1.getX(); if (Math.abs(diffX) > Math.abs(diffY)) { if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { if (diffX > 0) { if (getOnSwipeListener() != null) { getOnSwipeListener().onSwipeRight(); } } else { if (getOnSwipeListener() != null) { getOnSwipeListener().onSwipeLeft(); } } result = true; } } else if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { if (diffY > 0) { if (getOnSwipeListener() != null) { getOnSwipeListener().onSwipeBottom(); } } else { if (getOnSwipeListener() != null) { getOnSwipeListener().onSwipeTop(); } } result = true; } 

¿Cuál es la diferencia? Se establece el resultado = true, sólo si hemos comprobado que todos los requisitos (SWIPE_THRESHOLD y SWIPE_VELOCITY_THRESHOLD son Ok). Esto es importante si descartamos el golpe si algunos de los requisitos no se logran, y tenemos que hacer smth en el método onTouchEvent de OnSwipeTouchListener!

Aquí está el sencillo Android Code para detectar la dirección del gesto

En MainActivity.java y activity_main.xml , escriba el código siguiente:

MainActivity.java

 import java.util.ArrayList; import android.app.Activity; import android.gesture.Gesture; import android.gesture.GestureLibraries; import android.gesture.GestureLibrary; import android.gesture.GestureOverlayView; import android.gesture.GestureOverlayView.OnGesturePerformedListener; import android.gesture.GestureStroke; import android.gesture.Prediction; import android.os.Bundle; import android.widget.Toast; public class MainActivity extends Activity implements OnGesturePerformedListener { GestureOverlayView gesture; GestureLibrary lib; ArrayList<Prediction> prediction; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lib = GestureLibraries.fromRawResource(MainActivity.this, R.id.gestureOverlayView1); gesture = (GestureOverlayView) findViewById(R.id.gestureOverlayView1); gesture.addOnGesturePerformedListener(this); } @Override public void onGesturePerformed(GestureOverlayView overlay, Gesture gesture) { ArrayList<GestureStroke> strokeList = gesture.getStrokes(); // prediction = lib.recognize(gesture); float f[] = strokeList.get(0).points; String str = ""; if (f[0] < f[f.length - 2]) { str = "Right gesture"; } else if (f[0] > f[f.length - 2]) { str = "Left gesture"; } else { str = "no direction"; } Toast.makeText(getApplicationContext(), str, Toast.LENGTH_LONG).show(); } } 

Activity_main.xml

 <android.gesture.GestureOverlayView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:android1="http://schemas.android.com/apk/res/android" xmlns:android2="http://schemas.android.com/apk/res/android" android:id="@+id/gestureOverlayView1" android:layout_width="match_parent" android:layout_height="match_parent" android1:orientation="vertical" > <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Draw gesture" android:textAppearance="?android:attr/textAppearanceMedium" /> </android.gesture.GestureOverlayView> 

La última impl funciona sólo con esto:

  @Override public boolean onDown(MotionEvent e) { return true; } 

Si desea mostrar algunos botones con acciones cuando un elemento de lista es swipe son un montón de bibliotecas en Internet que tienen este comportamiento. Implementé la biblioteca que encontré en Internet y estoy muy satisfecho. Es muy simple de usar y muy rápido. He mejorado la biblioteca original y he añadido un nuevo oyente de clics para el clic de artículo. También agregué la biblioteca impresionante de la fuente ( http://fortawesome.github.io/Font-Awesome/ ) y ahora usted puede simplemente agregar un nuevo título del artículo y especificar el nombre del icono de la fuente impresionante.

Aquí está el enlace github

 public class TranslatorSwipeTouch implements OnTouchListener { private String TAG="TranslatorSwipeTouch"; @SuppressWarnings("deprecation") private GestureDetector detector=new GestureDetector(new TranslatorGestureListener()); @Override public boolean onTouch(View view, MotionEvent event) { return detector.onTouchEvent(event); } private class TranslatorGestureListener extends SimpleOnGestureListener { private final int GESTURE_THRESHOULD=100; private final int GESTURE_VELOCITY_THRESHOULD=100; @Override public boolean onDown(MotionEvent e) { return true; } @Override public boolean onFling(MotionEvent event1,MotionEvent event2,float velocityx,float velocityy) { try { float diffx=event2.getX()-event1.getX(); float diffy=event2.getY()-event1.getY(); if(Math.abs(diffx)>Math.abs(diffy)) { if(Math.abs(diffx)>GESTURE_THRESHOULD && Math.abs(velocityx)>GESTURE_VELOCITY_THRESHOULD) { if(diffx>0) { onSwipeRight(); } else { onSwipeLeft(); } } } else { if(Math.abs(diffy)>GESTURE_THRESHOULD && Math.abs(velocityy)>GESTURE_VELOCITY_THRESHOULD) { if(diffy>0) { onSwipeBottom(); } else { onSwipeTop(); } } } } catch(Exception e) { Log.d(TAG, ""+e.getMessage()); } return false; } public void onSwipeRight() { //Toast.makeText(this.getClass().get, "swipe right", Toast.LENGTH_SHORT).show(); Log.i(TAG, "Right"); } public void onSwipeLeft() { Log.i(TAG, "Left"); //Toast.makeText(MyActivity.this, "swipe left", Toast.LENGTH_SHORT).show(); } public void onSwipeTop() { Log.i(TAG, "Top"); //Toast.makeText(MyActivity.this, "swipe top", Toast.LENGTH_SHORT).show(); } public void onSwipeBottom() { Log.i(TAG, "Bottom"); //Toast.makeText(MyActivity.this, "swipe bottom", Toast.LENGTH_SHORT).show(); } } } 

No necesitas esos complicados cálculos. Sólo usando GestureDetector en su OnGestureListener dentro onFling :

  @Override public boolean onFling(final MotionEvent e1, final MotionEvent e2, final float velocityX, final float velocityY) { if ((velocityX * velocityX) > (velocityY * velocityY)) { if (velocityX < 0) { Log.i("TAG", "swipe left"); } else { Log.i("TAG", "swipe right"); } } else { if (velocityY < 0) { Log.i("TAG", "swipe up"); } else { Log.i("TAG", "swipe down"); } } return false; } 

El uso de la respuesta de Edward Brey en Kotlin

  view.setOnTouchListener(object: OnSwipeTouchListener(this) { override fun onSwipeLeft() { super.onSwipeLeft() } override fun onSwipeRight() { super.onSwipeRight() } } ) 
FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.