Android: cómo detectar cuándo ha terminado un pergamino

Estoy usando el método onScroll de GestureDetector.SimpleOnGestureListener para desplazar un bitmap grande en un canvas. Cuando el pergamino haya terminado, quiero volver a dibujar el bitmap en caso de que el usuario quiera desplazarse más … fuera del borde del bitmap, pero no puedo ver cómo detectar cuándo el pergamino ha finalizado (el usuario ha levantado el dedo de la pantalla).

e2.getAction () siempre parece devolver el valor 2, por lo que no es de ayuda. e2.getPressure parece devolver valores bastante constantes (alrededor de 0.25) hasta la llamada final al Despliegue cuando la presión parece caer a aproximadamente 0.13. Supongo que podría detectar esta reducción en la presión, pero esto estará lejos de ser infalible.

Debe haber una mejor manera: ¿alguien puede ayudar, por favor?

Así es como resolví el problema. Espero que esto ayude.

 // declare class member variables private GestureDetector mGestureDetector; private OnTouchListener mGestureListener; private boolean mIsScrolling = false; public void initGestureDetection() { // Gesture detection mGestureDetector = new GestureDetector(new SimpleOnGestureListener() { @Override public boolean onDoubleTap(MotionEvent e) { handleDoubleTap(e); return true; } @Override public boolean onSingleTapConfirmed(MotionEvent e) { handleSingleTap(e); return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { // i'm only scrolling along the X axis mIsScrolling = true; handleScroll(Math.round((e2.getX() - e1.getX()))); return true; } @Override /** * Don't know why but we need to intercept this guy and return true so that the other gestures are handled. * https://code.google.com/p/android/issues/detail?id=8233 */ public boolean onDown(MotionEvent e) { Log.d("GestureDetector --> onDown"); return true; } }); mGestureListener = new View.OnTouchListener() { public boolean onTouch(View v, MotionEvent event) { if (mGestureDetector.onTouchEvent(event)) { return true; } if(event.getAction() == MotionEvent.ACTION_UP) { if(mIsScrolling ) { Log.d("OnTouchListener --> onTouch ACTION_UP"); mIsScrolling = false; handleScrollFinished(); }; } return false; } }; // attach the OnTouchListener to the image view mImageView.setOnTouchListener(mGestureListener); } 

Deberías echarle un vistazo a http://developer.android.com/reference/android/widget/Scroller.html . Especialmente, esto podría ser útil (ordenado por relevancia):

 isFinished(); computeScrollOffset(); getFinalY(); getFinalX(); and getCurrY() getCurrX() getDuration() 

Esto implica que tienes que crear un Scroller.

Si desea usar el toque, también puede usar GestureDetector y definir su propio desplazamiento de canvas. El siguiente ejemplo crea un ScrollableImageView y para usarlo debe definir las medidas de su imagen. Puedes definir tu propio rango de desplazamiento y luego de finalizar el desplazamiento la imagen se vuelve a dibujar.

http://www.anddev.org/viewtopic.php?p=31487#31487

Dependiendo de tu código, debes considerar la invalidación (int l, int t, int r, int b); para la invalidación

 SimpleOnGestureListener.onFling() 

Parece tener lugar cuando termina un pergamino (es decir, el usuario suelta el dedo), eso es lo que estoy usando y funciona muy bien para mí.

Volviendo a esto después de unos meses, ahora he seguido una táctica diferente: usar un controlador (como en la muestra de Android Snake) para enviar un mensaje a la aplicación cada 125 milisegundos, lo que lo impulsa a verificar si se ha iniciado un desplazamiento y si ha transcurrido más de 100 milisegundos desde el último evento de desplazamiento.

Esto parece funcionar bastante bien, pero si alguien puede ver cualquier inconveniente o posible mejora, le agradeceré que me lo cuente.

El código relevante está en la clase MyView:

 public class MyView extends android.view.View { ... private long timeCheckInterval = 125; // milliseconds private long scrollEndInterval = 100; public long latestScrollEventTime; public boolean scrollInProgress = false; public MyView(Context context) { super(context); } private timeCheckHandler mTimeCheckHandler = new timeCheckHandler(); class timeCheckHandler extends Handler{ @Override public void handleMessage(Message msg) { long now = System.currentTimeMillis(); if (scrollInProgress && (now>latestScrollEventTime+scrollEndInterval)) { scrollInProgress = false; 

// El desplazamiento ha finalizado, así que inserte el código aquí

// que llama al método doDrawing ()

// para volver a dibujar el bitmap recentrado donde terminó el desplazamiento

  [ layout or view ].invalidate(); } this.sleep(timeCheckInterval); } public void sleep(long delayMillis) { this.removeMessages(0); sendMessageDelayed(obtainMessage(0), delayMillis); } } } @Override protected void onDraw(Canvas canvas){ super.onDraw(canvas); 

// código para dibujar un bitmap de gran tamaño en el canvas de la vista // posicionado para tener en cuenta cualquier desplazamiento que esté en curso

 } public void doDrawing() { 

// código para hacer un dibujo detallado (y que consume mucho tiempo) // en un bitmap de gran tamaño

// la siguiente instrucción restablece el reloj de Time Check // el reloj se inicia por primera vez cuando // la actividad principal llama a este método cuando se inicia la aplicación

  mTimeCheckHandler.sleep(timeCheckInterval); } 

// rest de la clase MyView

}

y en la clase MyGestureDetector

 public class MyGestureDetector extends SimpleOnGestureListener { @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { [MyView].scrollInProgress = true; long now = System.currentTimeMillis(); [MyView].latestScrollEventTime =now; [MyView].scrollX += (int) distanceX; [MyView].scrollY += (int) distanceY; 

// la siguiente instrucción hace que se llame al método onDraw de la Vista // que traza el bitmap del búfer en la pantalla // desplazado para tener en cuenta el desplazamiento

  [MyView].invalidate(); } 

// rest de la clase MyGestureDetector

}

Estaba investigando este mismo problema. Vi a Akos Cz responder a tu pregunta. Creé algo similar, pero con mi versión noté que solo funcionaba para un rollo normal, es decir, uno que no genera una aventura. Pero si se generó una aventura, independientemente de si procesé una aventura o no, entonces NO detectó “ACTION_UP” en “onTouchEvent”. Ahora bien, tal vez esto fue solo algo con mi implementación, pero si fuera así, no podría entender por qué.

Después de más investigaciones, noté que durante una aventura, el “ACTION_UP” pasaba a “onFling” en “e2” cada vez. Así que pensé que debe ser por eso que no se manejaba en “onTouchEvent” en esos casos.

Para hacerlo funcionar, solo tuve que llamar a un método para manejar “ACTION_UP” en “onFling” y luego funcionó para ambos tipos de desplazamiento. A continuación se detallan los pasos exactos que realicé para implementar en mi aplicación:

-inicializó un booleano “gestureScrolling” a “falso” en un constructor.

-Lo configuré como “verdadero” en “onScroll”

-creó un método para manejar el evento “ACTION_UP”. Dentro de ese evento, reinicié “gestureSCrolling” en falso y luego hice el rest del proceso que necesitaba hacer.

-in “onTouchEvent”, si se detectó “ACTION_UP” y “gestureScrolling” = true, llamé a mi método para manejar “ACTION_UP”

-Y la parte que hice que era diferente fue: también llamé a mi método para manejar “ACTION_UP” dentro de “onFling”.

Estoy seguro de que es demasiado tarde para ti, sin embargo, parece que he encontrado la solución correcta para tu pregunta original y no es necesaria la intención.

Si está utilizando el Scroller / OverScroller Object para desplazarse, debe verificar el valor de retorno de la siguiente función.

 public boolean computeScrollOffset() 

disfrutar

harvinder

No he hecho esto por mí mismo, pero mirando a Toque () siempre se obtiene una secuencia 0 <2> 1, por lo que el final tiene que ser un 1 para levantar los dedos.

No sé Android, pero mirando la documentación, parece que Rob tiene razón: Android ACTION_UP constante ¿ Intenta buscar ACTION_UP desde getAction ()?

Editar: ¿Qué muestra e1.getAction ()? ¿Alguna vez devuelve ACTION_UP? La documentación dice que contiene el evento de inactividad inicial, por lo que tal vez también notifique cuando el puntero esté activo

Editar: Solo dos cosas más que puedo pensar ¿Estás regresando falso en algún punto? Eso puede prevenir ACTION_UP

La única otra cosa que probaría es tener un evento separado, tal vez en Baja, y establecer una bandera dentro de Desplazamiento como isScrolling. Cuando ACTION_UP se da a onDown y se establece isScrolling, puede hacer lo que quiera y restablecer isScrolling en false. Es decir, suponiendo que se llama a Onown junto con onScroll, y getAction devolverá ACTION_UP durante onDown

No he probado / usado esto, sino una idea para un enfoque:

detener / interrumpir el rediseño del canvas en CADA evento de desplazamiento esperar 1s y luego comenzar a dibujar de nuevo canvas en CADA desplazamiento.

esto llevará a realizar el redibujado solo al final del scroll, ya que solo el último scroll no se interrumpirá para que se complete el redraw.

Espero que esta idea te ayude 🙂

Extracto del evento onScroll de GestureListener API: enlace de texto

public abstract boolean onScroll (MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) Desde: API Level 1

Devuelve * verdadero si el evento se consume, de lo contrario es falso

Tal vez una vez que se haya consumido el evento, la acción haya finalizado y el usuario haya quitado el dedo de la pantalla o al menos haya terminado esta acción de Despliegue.

Puede usar esto en una statement IF para buscar == verdadero y luego comenzar con la siguiente acción.

Creo que esto funcionará como lo necesites

 protected class SnappingGestureDetectorListener extends SimpleOnGestureListener{ @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY){ boolean result = super.onScroll(e1, e2, distanceX, distanceY); if(!result){ //Do what you need to do when the scrolling stop here } return result; } } 

Esto es lo que funcionó para mí.

He enriquecido el GestureDetector.OnGestureListener existente con el método onFingerUp () . Este oyente hace todo como el GestureDetector integrado y también puede escuchar el evento de arriba hacia arriba (no está enFling () ya que se llama solo cuando el dedo se levanta junto con una acción de deslizamiento rápido).

 import android.content.Context; import android.os.Handler; import android.view.GestureDetector; import android.view.MotionEvent; public class FingerUpGestureDetector extends GestureDetector { FingerUpGestureDetector.OnGestureListener fListener; public FingerUpGestureDetector(Context context, OnGestureListener listener) { super(context, listener); fListener = listener; } public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, OnGestureListener fListener) { super(context, listener); this.fListener = fListener; } public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, OnGestureListener fListener) { super(context, listener, handler); this.fListener = fListener; } public FingerUpGestureDetector(Context context, GestureDetector.OnGestureListener listener, Handler handler, boolean unused, OnGestureListener fListener) { super(context, listener, handler, unused); this.fListener = fListener; } public interface OnGestureListener extends GestureDetector.OnGestureListener { boolean onFingerUp(MotionEvent e); } public static class SimpleOnGestureListener extends GestureDetector.SimpleOnGestureListener implements FingerUpGestureDetector.OnGestureListener { @Override public boolean onFingerUp(MotionEvent e) { return false; } } @Override public boolean onTouchEvent(MotionEvent ev) { if (super.onTouchEvent(ev)) return true; if (ev.getAction() == MotionEvent.ACTION_UP) { return fListener.onFingerUp(ev); } return false; } } 

Mi bash de agregar funcionalidad adicional al detector de gestos. Espero que ayude a alguien a dedicar su tiempo a un mejor uso …

https://gist.github.com/WildOrangutan/043807ddbd68978179b7cea3b53c71e8