studio detector android listener gesture-recognition

android - detector - Detección de gestos fling en diseño de grillas.



gesture android studio (18)

¡El código del detector de movimientos de barrido anterior es muy útil! Sin embargo, es posible que desee hacer que esta densidad de la solución sea agnóstica utilizando los siguientes valores relativos (REL_SWIPE) lugar de los valores absolutos (SWIPE_)

DisplayMetrics dm = getResources().getDisplayMetrics(); int REL_SWIPE_MIN_DISTANCE = (int)(SWIPE_MIN_DISTANCE * dm.densityDpi / 160.0f); int REL_SWIPE_MAX_OFF_PATH = (int)(SWIPE_MAX_OFF_PATH * dm.densityDpi / 160.0f); int REL_SWIPE_THRESHOLD_VELOCITY = (int)(SWIPE_THRESHOLD_VELOCITY * dm.densityDpi / 160.0f);

Quiero que la detección de gestos de fling funcione en mi aplicación Android.

Lo que tengo es un GridLayout que contiene 9 ImageView s. La fuente se puede encontrar aquí: Diseño de cuadrícula de Romain Guys .

El archivo que tomo es de la aplicación Photostream de Romain Guy y solo ha sido ligeramente adaptado.

Para la simple situación de clics, solo necesito configurar onClickListener para cada ImageView que agrego para que sea la activity principal que implementa View.OnClickListener . Parece infinitamente más complicado implementar algo que reconozca una fling . Supongo que esto es porque puede abarcar views ?

  • Si mi actividad implementa OnGestureListener , no sé cómo establecer eso como el detector de gestos para la Grid o las vistas de Image que agrego.

    public class SelectFilterActivity extends Activity implements View.OnClickListener, OnGestureListener { ...

  • Si mi actividad implementa OnTouchListener entonces no tengo un método onFling para override (tiene dos eventos como parámetros que me permiten determinar si el lanzamiento fue notable).

    public class SelectFilterActivity extends Activity implements View.OnClickListener, OnTouchListener { ...

  • Si creo una View personalizada, como GestureImageView que amplía ImageView , no sé cómo decirle a la actividad que se ha producido un fling desde la vista. En cualquier caso, intenté esto y los métodos no se llamaron cuando toqué la pantalla.

Realmente solo necesito un ejemplo concreto de este trabajo en todos los puntos de vista. ¿Qué, cuándo y cómo debo adjuntar este listener ? También necesito poder detectar clics individuales.

// Gesture detection mGestureDetector = new GestureDetector(this, new GestureDetector.SimpleOnGestureListener() { public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { int dx = (int) (e2.getX() - e1.getX()); // don''t accept the fling if it''s too short // as it may conflict with a button push if (Math.abs(dx) > MAJOR_MOVE && Math.abs(velocityX) > Math.absvelocityY)) { if (velocityX > 0) { moveRight(); } else { moveLeft(); } return true; } else { return false; } } });

¿Es posible colocar una vista transparente sobre la parte superior de mi pantalla para capturar lanzamientos?

Si elijo no inflate mis vistas de imagen secundaria desde XML, ¿puedo pasar el GestureDetector como parámetro de constructor a una nueva subclase de ImageView que creo?

Esta es la actividad muy simple para la que estoy intentando que funcione la detección de archivos : SelectFilterActivity (Adaptado de photostream) .

He estado mirando estas fuentes:

Nada me ha funcionado hasta ahora y esperaba algunos consejos.


Esta es una respuesta combinada de las dos respuestas en la parte superior, si alguien quiere una implementación que funcione.

package com.yourapplication; import android.content.Context; import android.view.GestureDetector; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; public abstract class OnSwipeListener implements View.OnTouchListener { private final GestureDetector gestureDetector; public OnSwipeListener(Context context){ gestureDetector = new GestureDetector(context, new OnSwipeGestureListener(context)); gestureDetector.setIsLongpressEnabled(false); } @Override public boolean onTouch(View view, MotionEvent event) { return gestureDetector.onTouchEvent(event); } private final class OnSwipeGestureListener extends GestureDetector.SimpleOnGestureListener { private final int minSwipeDelta; private final int minSwipeVelocity; private final int maxSwipeVelocity; private OnSwipeGestureListener(Context context) { ViewConfiguration configuration = ViewConfiguration.get(context); // We think a swipe scrolls a full page. //minSwipeDelta = configuration.getScaledTouchSlop(); minSwipeDelta = configuration.getScaledPagingTouchSlop(); minSwipeVelocity = configuration.getScaledMinimumFlingVelocity(); maxSwipeVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override public boolean onDown(MotionEvent event) { // Return true because we want system to report subsequent events to us. return true; } // NOTE: see http://.com/questions/937313/android-basic-gesture-detection @Override public boolean onFling(MotionEvent event1, MotionEvent event2, float velocityX, float velocityY) { boolean result = false; try { float deltaX = event2.getX() - event1.getX(); float deltaY = event2.getY() - event1.getY(); float absVelocityX = Math.abs(velocityX); float absVelocityY = Math.abs(velocityY); float absDeltaX = Math.abs(deltaX); float absDeltaY = Math.abs(deltaY); if (absDeltaX > absDeltaY) { if (absDeltaX > minSwipeDelta && absVelocityX > minSwipeVelocity && absVelocityX < maxSwipeVelocity) { if (deltaX < 0) { onSwipeLeft(); } else { onSwipeRight(); } } result = true; } else if (absDeltaY > minSwipeDelta && absVelocityY > minSwipeVelocity && absVelocityY < maxSwipeVelocity) { if (deltaY < 0) { onSwipeTop(); } else { onSwipeBottom(); } } result = true; } catch (Exception e) { e.printStackTrace(); } return result; } } public void onSwipeLeft() {} public void onSwipeRight() {} public void onSwipeTop() {} public void onSwipeBottom() {} }


Esta pregunta es bastante antigua y, en julio de 2011, Google lanzó el Paquete de compatibilidad, revisión 3, que incluye el ViewPager que funciona con Android 1.6 en ViewPager . Las respuestas de GestureListener publicadas para esta pregunta no se sienten muy elegantes en Android. Si está buscando el código utilizado para cambiar entre las fotos en la Galería de Android o para cambiar las vistas en la nueva aplicación Play Market, definitivamente es ViewPager .

Aquí hay algunos enlaces para más información:


Gracias a Code Shogun , cuyo código me adapté a mi situación.

Deje que su actividad implemente OnClickListener como siempre:

public class SelectFilterActivity extends Activity implements OnClickListener { private static final int SWIPE_MIN_DISTANCE = 120; private static final int SWIPE_MAX_OFF_PATH = 250; private static final int SWIPE_THRESHOLD_VELOCITY = 200; private GestureDetector gestureDetector; View.OnTouchListener gestureListener; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* ... */ // Gesture detection gestureDetector = new GestureDetector(this, new MyGestureDetector()); gestureListener = new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { return gestureDetector.onTouchEvent(event); } }; } class MyGestureDetector extends SimpleOnGestureListener { @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { try { if (Math.abs(e1.getY() - e2.getY()) > SWIPE_MAX_OFF_PATH) return false; // right to left swipe if(e1.getX() - e2.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { Toast.makeText(SelectFilterActivity.this, "Left Swipe", Toast.LENGTH_SHORT).show(); } else if (e2.getX() - e1.getX() > SWIPE_MIN_DISTANCE && Math.abs(velocityX) > SWIPE_THRESHOLD_VELOCITY) { Toast.makeText(SelectFilterActivity.this, "Right Swipe", Toast.LENGTH_SHORT).show(); } } catch (Exception e) { // nothing } return false; } @Override public boolean onDown(MotionEvent e) { return true; } } }

Adjunte su escucha de gestos a todas las vistas que agregue al diseño principal;

// Do this for each view added to the grid imageView.setOnClickListener(SelectFilterActivity.this); imageView.setOnTouchListener(gestureListener);

Observe con asombro a medida que sus métodos anulados son alcanzados, tanto el onClick(View v) de la actividad como el onFling del oyente de gestos.

public void onClick(View v) { Filter f = (Filter) v.getTag(); FilterFullscreenActivity.show(this, input, f); }

El post ''fling'' dance es opcional pero animado.


Hay algunas propuestas en la web (y esta página) para usar ViewConfiguration. getScaledTouchSlop () tiene un valor escalado del dispositivo para SWIPE_MIN_DISTANCE .

getScaledTouchSlop() está diseñado para la distancia de "umbral de desplazamiento ", no para deslizar. La distancia del umbral de desplazamiento tiene que ser más pequeña que la distancia del umbral de "oscilación entre páginas". Por ejemplo, esta función devuelve 12 píxeles en mi Samsung GS2, y los ejemplos citados en esta página son alrededor de 100 píxeles.

Con API Level 8 (Android 2.2, Froyo), tienes getScaledPagingTouchSlop() , destinado a deslizar páginas. En mi dispositivo, devuelve 24 (píxeles). Entonces, si estás en el nivel de API <8, creo que "2 * getScaledTouchSlop() " debería ser el umbral de deslizamiento "estándar". Pero los usuarios de mi aplicación con pantallas pequeñas me dijeron que era muy poco ... Como en mi aplicación, puede desplazarse verticalmente y cambiar de página horizontalmente. Con el valor propuesto, a veces cambian de página en lugar de desplazarse.


Hay mucha información excelente aquí. Desafortunadamente, gran parte de este código de procesamiento de lanzamientos está disperso en varios sitios en varios estados de finalización, aunque uno podría pensar que esto es esencial para muchas aplicaciones.

Me he tomado el tiempo para crear un oyente de lanzamientos que verifique que se cumplan las condiciones apropiadas. He agregado un servicio de escucha de páginas que agrega más controles para garantizar que los lanzamientos cumplan con el umbral para los lanzamientos de páginas. Ambos escuchas le permiten restringir fácilmente los lanzamientos al eje horizontal o vertical. Puedes ver cómo se usa en una vista para imágenes deslizantes . Reconozco que la gente aquí ha hecho la mayor parte de la investigación, simplemente la he reunido en una biblioteca utilizable.

Estos últimos días representan mi primer intento de codificación en Android; esperar mucho más por venir.


Hay una interfaz incorporada que puedes usar directamente para todos los gestos:
Aquí hay una explicación para un usuario de nivel básico: Hay 2 importaciones, tenga cuidado al elegir que ambas son diferentes.


Lo hago un poco diferente y escribí una clase de detector adicional que implementa View.onTouchListener

onCreate es simplemente agregarlo al diseño más bajo como este:

ActivitySwipeDetector activitySwipeDetector = new ActivitySwipeDetector(this); lowestLayout = (RelativeLayout)this.findViewById(R.id.lowestLayout); lowestLayout.setOnTouchListener(activitySwipeDetector);

donde id.lowestLayout es el id.xxx para la vista más baja en la jerarquía de diseño y lowerLayout se declara como RelativeLayout

Y luego está la clase de detector de deslizamiento de actividad real:

public class ActivitySwipeDetector implements View.OnTouchListener { static final String logTag = "ActivitySwipeDetector"; private Activity activity; static final int MIN_DISTANCE = 100; private float downX, downY, upX, upY; public ActivitySwipeDetector(Activity activity){ this.activity = activity; } public void onRightSwipe(){ Log.i(logTag, "RightToLeftSwipe!"); activity.doSomething(); } public void onLeftSwipe(){ Log.i(logTag, "LeftToRightSwipe!"); activity.doSomething(); } public void onDownSwipe(){ Log.i(logTag, "onTopToBottomSwipe!"); activity.doSomething(); } public void onUpSwipe(){ Log.i(logTag, "onBottomToTopSwipe!"); activity.doSomething(); } public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { downX = event.getX(); downY = event.getY(); return true; } case MotionEvent.ACTION_UP: { upX = event.getX(); upY = event.getY(); float deltaX = downX - upX; float deltaY = downY - upY; // swipe horizontal? if(Math.abs(deltaX) > Math.abs(deltaY)) { if(Math.abs(deltaX) > MIN_DISTANCE){ // left or right if(deltaX > 0) { this.onRightSwipe(); return true; } if(deltaX < 0) { this.onLeftSwipe(); return true; } } else { Log.i(logTag, "Horizontal Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE); return false; // We don''t consume the event } } // swipe vertical? else { if(Math.abs(deltaY) > MIN_DISTANCE){ // top or down if(deltaY < 0) { this.onDownSwipe(); return true; } if(deltaY > 0) { this.onUpSwipe(); return true; } } else { Log.i(logTag, "Vertical Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE); return false; // We don''t consume the event } } return true; } } return false; } }

Funciona muy bien para mí!


Mi versión de solución propuesta por Thomas Fankhauser y Marek Sebera (no maneja barridos verticales):

SwipeInterface.java

import android.view.View; public interface SwipeInterface { public void onLeftToRight(View v); public void onRightToLeft(View v); }

ActivitySwipeDetector.java

import android.content.Context; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; public class ActivitySwipeDetector implements View.OnTouchListener { static final String logTag = "ActivitySwipeDetector"; private SwipeInterface activity; private float downX, downY; private long timeDown; private final float MIN_DISTANCE; private final int VELOCITY; private final float MAX_OFF_PATH; public ActivitySwipeDetector(Context context, SwipeInterface activity){ this.activity = activity; final ViewConfiguration vc = ViewConfiguration.get(context); DisplayMetrics dm = context.getResources().getDisplayMetrics(); MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density; VELOCITY = vc.getScaledMinimumFlingVelocity(); MAX_OFF_PATH = MIN_DISTANCE * 2; } public void onRightToLeftSwipe(View v){ Log.i(logTag, "RightToLeftSwipe!"); activity.onRightToLeft(v); } public void onLeftToRightSwipe(View v){ Log.i(logTag, "LeftToRightSwipe!"); activity.onLeftToRight(v); } public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { Log.d("onTouch", "ACTION_DOWN"); timeDown = System.currentTimeMillis(); downX = event.getX(); downY = event.getY(); return true; } case MotionEvent.ACTION_UP: { Log.d("onTouch", "ACTION_UP"); long timeUp = System.currentTimeMillis(); float upX = event.getX(); float upY = event.getY(); float deltaX = downX - upX; float absDeltaX = Math.abs(deltaX); float deltaY = downY - upY; float absDeltaY = Math.abs(deltaY); long time = timeUp - timeDown; if (absDeltaY > MAX_OFF_PATH) { Log.i(logTag, String.format("absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY, MAX_OFF_PATH)); return v.performClick(); } final long M_SEC = 1000; if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) { if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; } if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; } } else { Log.i(logTag, String.format("absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b", absDeltaX, MIN_DISTANCE, (absDeltaX > MIN_DISTANCE))); Log.i(logTag, String.format("absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b", absDeltaX, time, VELOCITY, time * VELOCITY / M_SEC, (absDeltaX > time * VELOCITY / M_SEC))); } } } return false; } }


Puede usar la biblioteca droidQuery para manejar archivos, clics, clics largos y eventos personalizados. La implementación se basa en mi respuesta anterior a continuación, pero droidQuery proporciona una sintaxis simple y elegante:

//global variables private boolean isSwiping = false; private SwipeDetector.Direction swipeDirection = null; private View v;//must be instantiated before next call. //swipe-handling code $.with(v).swipe(new Function() { @Override public void invoke($ droidQuery, Object... params) { if (params[0] == SwipeDetector.Direction.START) isSwiping = true; else if (params[0] == SwipeDetector.Direction.STOP) { if (isSwiping) { isSwiping = false; if (swipeDirection != null) { switch(swipeDirection) { case DOWN : //TODO: Down swipe complete, so do something break; case UP : //TODO: Up swipe complete, so do something break; case LEFT : //TODO: Left swipe complete, so do something break; case RIGHT : //TODO: Right swipe complete, so do something break; default : break; } } } } else { swipeDirection = (SwipeDetector.Direction) params[0]; } } });

Respuesta original

Esta respuesta utiliza una combinación de componentes de las otras respuestas aquí. Consiste en la clase SwipeDetector , que tiene una interfaz interna para escuchar eventos. También proporciono un RelativeLayout para mostrar cómo anular el método onTouch una View para permitir eventos de barrido y otros eventos detectados (como clics o clics largos).

SwipeDetector

package self.philbrown; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; /** * Detect Swipes on a per-view basis. Based on original code by Thomas Fankhauser on .com, * with adaptations by other authors (see link). * @author Phil Brown * @see <a href="http://.com/questions/937313/android-basic-gesture-detection">android-basic-gesture-detection</a> */ public class SwipeDetector implements View.OnTouchListener { /** * The minimum distance a finger must travel in order to register a swipe event. */ private int minSwipeDistance; /** Maintains a reference to the first detected down touch event. */ private float downX, downY; /** Maintains a reference to the first detected up touch event. */ private float upX, upY; /** provides access to size and dimension contants */ private ViewConfiguration config; /** * provides callbacks to a listener class for various swipe gestures. */ private SwipeListener listener; public SwipeDetector(SwipeListener listener) { this.listener = listener; } /** * {@inheritDoc} */ public boolean onTouch(View v, MotionEvent event) { if (config == null) { config = ViewConfiguration.get(v.getContext()); minSwipeDistance = config.getScaledTouchSlop(); } switch(event.getAction()) { case MotionEvent.ACTION_DOWN: downX = event.getX(); downY = event.getY(); return true; case MotionEvent.ACTION_UP: upX = event.getX(); upY = event.getY(); float deltaX = downX - upX; float deltaY = downY - upY; // swipe horizontal? if(Math.abs(deltaX) > minSwipeDistance) { // left or right if (deltaX < 0) { if (listener != null) { listener.onRightSwipe(v); return true; } } if (deltaX > 0) { if (listener != null) { listener.onLeftSwipe(v); return true; } } } // swipe vertical? if(Math.abs(deltaY) > minSwipeDistance) { // top or down if (deltaY < 0) { if (listener != null) { listener.onDownSwipe(v); return true; } } if (deltaY > 0) { if (listener != null) { listener.onUpSwipe(v); return true; } } } } return false; } /** * Provides callbacks to a registered listener for swipe events in {@link SwipeDetector} * @author Phil Brown */ public interface SwipeListener { /** Callback for registering a new swipe motion from the bottom of the view toward its top. */ public void onUpSwipe(View v); /** Callback for registering a new swipe motion from the left of the view toward its right. */ public void onRightSwipe(View v); /** Callback for registering a new swipe motion from the right of the view toward its left. */ public void onLeftSwipe(View v); /** Callback for registering a new swipe motion from the top of the view toward its bottom. */ public void onDownSwipe(View v); } }

Vista de interceptor de deslizamiento

package self.philbrown; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.widget.RelativeLayout; import com.npeinc.module_NPECore.model.SwipeDetector; import com.npeinc.module_NPECore.model.SwipeDetector.SwipeListener; /** * View subclass used for handling all touches (swipes and others) * @author Phil Brown */ public class SwipeInterceptorView extends RelativeLayout { private SwipeDetector swiper = null; public void setSwipeListener(SwipeListener listener) { if (swiper == null) swiper = new SwipeDetector(listener); } public SwipeInterceptorView(Context context) { super(context); } public SwipeInterceptorView(Context context, AttributeSet attrs) { super(context, attrs); } public SwipeInterceptorView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } @Override public boolean onTouchEvent(MotionEvent e) { boolean swipe = false, touch = false; if (swiper != null) swipe = swiper.onTouch(this, e); touch = super.onTouchEvent(e); return swipe || touch; } }


Solución ligeramente modificada y reparada de Thomas Fankhauser

El sistema completo consta de dos archivos, SwipeInterface y ActivitySwipeDetector

SwipeInterface.java

import android.view.View; public interface SwipeInterface { public void bottom2top(View v); public void left2right(View v); public void right2left(View v); public void top2bottom(View v); }

Detector

import android.util.Log; import android.view.MotionEvent; import android.view.View; public class ActivitySwipeDetector implements View.OnTouchListener { static final String logTag = "ActivitySwipeDetector"; private SwipeInterface activity; static final int MIN_DISTANCE = 100; private float downX, downY, upX, upY; public ActivitySwipeDetector(SwipeInterface activity){ this.activity = activity; } public void onRightToLeftSwipe(View v){ Log.i(logTag, "RightToLeftSwipe!"); activity.right2left(v); } public void onLeftToRightSwipe(View v){ Log.i(logTag, "LeftToRightSwipe!"); activity.left2right(v); } public void onTopToBottomSwipe(View v){ Log.i(logTag, "onTopToBottomSwipe!"); activity.top2bottom(v); } public void onBottomToTopSwipe(View v){ Log.i(logTag, "onBottomToTopSwipe!"); activity.bottom2top(v); } public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { downX = event.getX(); downY = event.getY(); return true; } case MotionEvent.ACTION_UP: { upX = event.getX(); upY = event.getY(); float deltaX = downX - upX; float deltaY = downY - upY; // swipe horizontal? if(Math.abs(deltaX) > MIN_DISTANCE){ // left or right if(deltaX < 0) { this.onLeftToRightSwipe(v); return true; } if(deltaX > 0) { this.onRightToLeftSwipe(v); return true; } } else { Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE); } // swipe vertical? if(Math.abs(deltaY) > MIN_DISTANCE){ // top or down if(deltaY < 0) { this.onTopToBottomSwipe(v); return true; } if(deltaY > 0) { this.onBottomToTopSwipe(v); return true; } } else { Log.i(logTag, "Swipe was only " + Math.abs(deltaX) + " long, need at least " + MIN_DISTANCE); v.performClick(); } } } return false; } }

se usa así:

ActivitySwipeDetector swipe = new ActivitySwipeDetector(this); LinearLayout swipe_layout = (LinearLayout) findViewById(R.id.swipe_layout); swipe_layout.setOnTouchListener(swipe);

Y al implementar la Activity , debe implementar métodos desde SwipeInterface , y puede averiguar a qué Vista se llamó el Evento de Swipe .

@Override public void left2right(View v) { switch(v.getId()){ case R.id.swipe_layout: // do your stuff here break; } }


También como una mejora menor.

La razón principal para el bloque try / catch es que e1 podría ser nulo para el movimiento inicial. Además de la opción try / catch, incluya una prueba para null y return. similar al siguiente

if (e1 == null || e2 == null) return false; try { ... } catch (Exception e) {} return false;


Una de las respuestas anteriores menciona el manejo de una densidad de píxeles diferente, pero sugiere calcular los parámetros de deslizamiento a mano. Vale la pena señalar que realmente puede obtener valores escalables y razonables del sistema utilizando la clase ViewConfiguration :

final ViewConfiguration vc = ViewConfiguration.get(getContext()); final int swipeMinDistance = vc.getScaledPagingTouchSlop(); final int swipeThresholdVelocity = vc.getScaledMinimumFlingVelocity(); final int swipeMaxOffPath = vc.getScaledTouchSlop(); // (there is also vc.getScaledMaximumFlingVelocity() one could check against)

Noté que el uso de estos valores hace que la "sensación" de la aventura sea más consistente entre la aplicación y el resto del sistema.


A todos: no se olviden del caso MotionEvent.ACTION_CANCEL:

llama en 30% swipes sin ACTION_UP

y es igual a ACTION_UP en este caso


Los gestos son esos movimientos sutiles para desencadenar interacciones entre la pantalla táctil y el usuario. Dura el tiempo entre el primer toque en la pantalla hasta el punto en que el último dedo sale de la superficie.

Android nos ofrece una clase llamada GestureDetector mediante el cual podemos detectar gestos comunes como tocando abajo y arriba, deslizando vertical y horizontalmente (aventura), pulsación larga y corta, grifos dobles, etc . y adjuntar a los oyentes a ellos.

Haga que nuestra clase de actividad implemente GestureDetector.OnDoubleTapListener (para detección de gestos de doble toque) y GestureDetector.OnGestureListener e implemente todos los métodos abstractos. Para obtener más información. puede visitar https://developer.android.com/training/gestures/detector.html . Courtesy

Para la prueba de demostración. GestureDetectorDemo


Me inscribí en una Clase más genérica, tomé la clase de Tomas y agregué una Interfaz que envía eventos a tu Actividad o Fragmento. registrará el oyente en el constructor, así que asegúrese de implementar la interfaz o se obtendrá una excepción ClassCastException. la interfaz devuelve uno de los cuatro int finales definidos en la clase y devolverá la vista en la que se activó.

import android.app.Activity; import android.support.v4.app.Fragment; import android.util.Log; import android.view.MotionEvent; import android.view.View; public class SwipeDetector implements View.OnTouchListener{ static final int MIN_DISTANCE = 100; private float downX, downY, upX, upY; public final static int RIGHT_TO_LEFT=1; public final static int LEFT_TO_RIGHT=2; public final static int TOP_TO_BOTTOM=3; public final static int BOTTOM_TO_TOP=4; private View v; private onSwipeEvent swipeEventListener; public SwipeDetector(Activity activity,View v){ try{ swipeEventListener=(onSwipeEvent)activity; } catch(ClassCastException e) { Log.e("ClassCastException",activity.toString()+" must implement SwipeDetector.onSwipeEvent"); } this.v=v; } public SwipeDetector(Fragment fragment,View v){ try{ swipeEventListener=(onSwipeEvent)fragment; } catch(ClassCastException e) { Log.e("ClassCastException",fragment.toString()+" must implement SwipeDetector.onSwipeEvent"); } this.v=v; } public void onRightToLeftSwipe(){ swipeEventListener.SwipeEventDetected(v,RIGHT_TO_LEFT); } public void onLeftToRightSwipe(){ swipeEventListener.SwipeEventDetected(v,LEFT_TO_RIGHT); } public void onTopToBottomSwipe(){ swipeEventListener.SwipeEventDetected(v,TOP_TO_BOTTOM); } public void onBottomToTopSwipe(){ swipeEventListener.SwipeEventDetected(v,BOTTOM_TO_TOP); } public boolean onTouch(View v, MotionEvent event) { switch(event.getAction()){ case MotionEvent.ACTION_DOWN: { downX = event.getX(); downY = event.getY(); return true; } case MotionEvent.ACTION_UP: { upX = event.getX(); upY = event.getY(); float deltaX = downX - upX; float deltaY = downY - upY; //HORIZONTAL SCROLL if(Math.abs(deltaX) > Math.abs(deltaY)) { if(Math.abs(deltaX) > MIN_DISTANCE){ // left or right if(deltaX < 0) { this.onLeftToRightSwipe(); return true; } if(deltaX > 0) { this.onRightToLeftSwipe(); return true; } } else { //not long enough swipe... return false; } } //VERTICAL SCROLL else { if(Math.abs(deltaY) > MIN_DISTANCE){ // top or down if(deltaY < 0) { this.onTopToBottomSwipe(); return true; } if(deltaY > 0) { this.onBottomToTopSwipe(); return true; } } else { //not long enough swipe... return false; } } return true; } } return false; } public interface onSwipeEvent { public void SwipeEventDetected(View v , int SwipeType); } }


Sé que es demasiado tarde para responder, pero aún estoy publicando Swipe Detection for ListView que Cómo usar Swipe Touch Listener en ListView Item .

Refrence: Exterminator13 (uno de respuesta en esta página)

Hacer un ActivitySwipeDetector.class

package com.example.wocketapp; import android.content.Context; import android.util.DisplayMetrics; import android.util.Log; import android.view.MotionEvent; import android.view.View; import android.view.ViewConfiguration; public class ActivitySwipeDetector implements View.OnTouchListener { static final String logTag = "SwipeDetector"; private SwipeInterface activity; private float downX, downY; private long timeDown; private final float MIN_DISTANCE; private final int VELOCITY; private final float MAX_OFF_PATH; public ActivitySwipeDetector(Context context, SwipeInterface activity) { this.activity = activity; final ViewConfiguration vc = ViewConfiguration.get(context); DisplayMetrics dm = context.getResources().getDisplayMetrics(); MIN_DISTANCE = vc.getScaledPagingTouchSlop() * dm.density; VELOCITY = vc.getScaledMinimumFlingVelocity(); MAX_OFF_PATH = MIN_DISTANCE * 2; } public void onRightToLeftSwipe(View v) { Log.i(logTag, "RightToLeftSwipe!"); activity.onRightToLeft(v); } public void onLeftToRightSwipe(View v) { Log.i(logTag, "LeftToRightSwipe!"); activity.onLeftToRight(v); } public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: { Log.d("onTouch", "ACTION_DOWN"); timeDown = System.currentTimeMillis(); downX = event.getX(); downY = event.getY(); v.getParent().requestDisallowInterceptTouchEvent(false); return true; } case MotionEvent.ACTION_MOVE: { float y_up = event.getY(); float deltaY = y_up - downY; float absDeltaYMove = Math.abs(deltaY); if (absDeltaYMove > 60) { v.getParent().requestDisallowInterceptTouchEvent(false); } else { v.getParent().requestDisallowInterceptTouchEvent(true); } } break; case MotionEvent.ACTION_UP: { Log.d("onTouch", "ACTION_UP"); long timeUp = System.currentTimeMillis(); float upX = event.getX(); float upY = event.getY(); float deltaX = downX - upX; float absDeltaX = Math.abs(deltaX); float deltaY = downY - upY; float absDeltaY = Math.abs(deltaY); long time = timeUp - timeDown; if (absDeltaY > MAX_OFF_PATH) { Log.e(logTag, String.format( "absDeltaY=%.2f, MAX_OFF_PATH=%.2f", absDeltaY, MAX_OFF_PATH)); return v.performClick(); } final long M_SEC = 1000; if (absDeltaX > MIN_DISTANCE && absDeltaX > time * VELOCITY / M_SEC) { v.getParent().requestDisallowInterceptTouchEvent(true); if (deltaX < 0) { this.onLeftToRightSwipe(v); return true; } if (deltaX > 0) { this.onRightToLeftSwipe(v); return true; } } else { Log.i(logTag, String.format( "absDeltaX=%.2f, MIN_DISTANCE=%.2f, absDeltaX > MIN_DISTANCE=%b", absDeltaX, MIN_DISTANCE, (absDeltaX > MIN_DISTANCE))); Log.i(logTag, String.format( "absDeltaX=%.2f, time=%d, VELOCITY=%d, time*VELOCITY/M_SEC=%d, absDeltaX > time * VELOCITY / M_SEC=%b", absDeltaX, time, VELOCITY, time * VELOCITY / M_SEC, (absDeltaX > time * VELOCITY / M_SEC))); } v.getParent().requestDisallowInterceptTouchEvent(false); } } return false; } public interface SwipeInterface { public void onLeftToRight(View v); public void onRightToLeft(View v); } }

Llámalo de tu clase de actividad así:

yourLayout.setOnTouchListener(new ActivitySwipeDetector(this, your_activity.this));

Y no te olvides de implementar SwipeInterface que te dará dos métodos @override:

@Override public void onLeftToRight(View v) { Log.e("TAG", "L to R"); } @Override public void onRightToLeft(View v) { Log.e("TAG", "R to L"); }


Si no te gusta crear una clase separada o crear un complejo de código,
puedes crear una variable GestureDetector dentro de OnTouchListener y hacer que tu código sea más fácil

namVyuVar puede ser cualquier nombre de la Vista en la que necesita configurar el listner

namVyuVar.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent MsnEvtPsgVal) { flingActionVar.onTouchEvent(MsnEvtPsgVal); return true; } GestureDetector flingActionVar = new GestureDetector(getApplicationContext(), new GestureDetector.SimpleOnGestureListener() { private static final int flingActionMinDstVac = 120; private static final int flingActionMinSpdVac = 200; @Override public boolean onFling(MotionEvent fstMsnEvtPsgVal, MotionEvent lstMsnEvtPsgVal, float flingActionXcoSpdPsgVal, float flingActionYcoSpdPsgVal) { if(fstMsnEvtPsgVal.getX() - lstMsnEvtPsgVal.getX() > flingActionMinDstVac && Math.abs(flingActionXcoSpdPsgVal) > flingActionMinSpdVac) { // TskTdo :=> On Right to Left fling return false; } else if (lstMsnEvtPsgVal.getX() - fstMsnEvtPsgVal.getX() > flingActionMinDstVac && Math.abs(flingActionXcoSpdPsgVal) > flingActionMinSpdVac) { // TskTdo :=> On Left to Right fling return false; } if(fstMsnEvtPsgVal.getY() - lstMsnEvtPsgVal.getY() > flingActionMinDstVac && Math.abs(flingActionYcoSpdPsgVal) > flingActionMinSpdVac) { // TskTdo :=> On Bottom to Top fling return false; } else if (lstMsnEvtPsgVal.getY() - fstMsnEvtPsgVal.getY() > flingActionMinDstVac && Math.abs(flingActionYcoSpdPsgVal) > flingActionMinSpdVac) { // TskTdo :=> On Top to Bottom fling return false; } return false; } }); });