2011-10-28 10 views
16

En una aplicación en la que estoy trabajando, tengo el requisito de que un usuario debe hacer clic en & mantener un componente durante un período de tiempo antes de que se produzca una determinada acción.El evento LongClick ocurre demasiado rápido. ¿Cómo puedo aumentar el tiempo de clic requerido para activarlo?

Actualmente estoy usando un OnLongClickListener para escuchar el longclick, pero creo que la longitud de un clic para activar el evento OnLongClick es demasiado corta.

Por ejemplo, supongamos que el evento LongClick se activa después de un clic de 400ms, pero quiero que el usuario tenga que hacer clic en & mantener durante 1200ms antes de que se active el evento.

¿Hay alguna manera en que pueda configurar el evento LongClick para que requiera un clic más largo?
¿O tal vez haya otra construcción que me permita escuchar clics más largos?

Respuesta

17

No es posible cambiar el temporizador en el evento onLongClick, es administrado por el propio Android.

Lo que es posible es usar .setOnTouchListener().

Luego, regístrese cuando MotionEvent sea ACTION_DOWN.
Tenga en cuenta la hora actual en una variable.
Luego, cuando se registra un MotionEvent con ACTION_UP y current_time - actionDown time> 1200 ms, entonces haga algo.

por lo menos:

Button button = new Button(); 
long then = 0; 
    button.setOnTouchListener(new OnTouchListener() { 

     @Override 
     public boolean onTouch(View v, MotionEvent event) { 
      if(event.getAction() == MotionEvent.ACTION_DOWN){ 
       then = (Long) System.currentTimeMillis(); 
      } 
      else if(event.getAction() == MotionEvent.ACTION_UP){ 
       if(((Long) System.currentTimeMillis() - then) > 1200){ 
        return true; 
       } 
      } 
      return false; 
     } 
    }) 
+0

Esto casi hace lo que necesito hacer. El enfoque publicado solo ejecuta el código cuando el usuario levanta el dedo. Me gustaría que mi lógica de negocios se ejecute tan pronto como se demore, en lugar de cuando el usuario deje de presionar. – ampersandre

+0

¿Por qué se comenta entonces = getSystemTime()? También el método getSystemTime() no funciona. Quiso decir: System.currentTimeMillis() –

+0

Confirmado. Defina entonces como un largo, no como un int. Luego use: then = (Long) System.currentTimeMillis(); y cuando encuentre la diferencia, asegúrese de volver a emitir demasiado cuando use System.currentTimeMillis() en lugar de getSystemTime() –

3

trabajé encontrar una solución con la ayuda de Rohan :)
Adapté su respuesta para adaptarse a mis necesidades.

Cuando el usuario presiona el botón, se inicia una secuencia. El hilo duerme para mi retraso deseado, y cuando se despierta, ejecuta el código que necesito que haga. Cuando el usuario suelta, el hilo se elimina. Esto logra lo que quiero, porque si el usuario lo suelta antes de que el hilo se despierte, el hilo se interrumpe y la acción no se produce.

Me gusta este enfoque porque me permite ejecutar mi lógica de negocio tan pronto como se demora, lo que es bueno porque puedo darle al usuario algunos comentarios haciéndoles saber que han presionado lo suficiente (el teléfono puede vibrar, por ejemplo).
La desventaja de este enfoque es: existe el riesgo de que el usuario suelte el botón mientras se ejecuta la acción deseada, y elimina el hilo antes de que todo esté hecho. Este no es un gran problema para mi caso, porque mi lógica comercial hace muy poco; solo activa un evento para que otra clase lo procese. Si la acción no se completó por completo, es aceptable que el usuario tenga que volver a intentarlo.

El código es un poco más largo de lo que me gustaría, pero si esta es una característica común en su aplicación, es fácilmente reutilizable. Aquí está un ejemplo de código:

protected class MyLongClickListener implements View.OnTouchListener { 
    private Thread longClickSensor; 

    public boolean onTouch(View view, MotionEvent event) { 
     // If the user is pressing down and there is no thread, make one and start it 
     if (event.getAction() == MotionEvent.ACTION_DOWN && longClickSensor == null) { 
      longClickSensor = new Thread(new MyDelayedAction()); 
      longClickSensor.start(); 
     } 
     // If the user has let go and there was a thread, stop it and forget about the thread 
     if (event.getAction() == MotionEvent.ACTION_UP && longClickSensor != null) { 
      longClickSensor.interrupt(); 
      longClickSensor = null; 
     } 
     return false; 
    } 

    private class MyDelayedAction implements Runnable { 
     private final long delayMs = 1200; 

     public void run() { 
      try { 
       Thread.sleep(delayMs); // Sleep for a while 
       doBusinessLogic();  // If the thread is still around after the sleep, do the work 
      } catch (InterruptedException e) { return; } 
     } 
     private void doBusinessLogic() { 
      // Make sure this logic is as quick as possible, or delegate it to some other class 
      // through Broadcasted Intents, because if the user lets go while the work is happenening, 
      // the thread will be interrupted. 
     } 
    } 
} 
+1

Esto se volverá complicado, cuando el usuario usa un segundo dedo. debe verificar, si ya hay un hilo iniciado o debe hacer un seguimiento de los diferentes identificadores de selector. – xeed

2
final boolean[] isLongPress = {false}; 
final int duration = 3000; 
final Handler someHandler = new Handler(); 
    final Runnable someCall = new Runnable() { 
     @Override 
     public void run() { 
      if(isLongPress[0]) { 
       // your code goes here 
      } 
     } 
    }; 

    someButton.setOnTouchListener(new View.OnTouchListener() { 

     @Override 
     public boolean onTouch(View v, MotionEvent event) { 
      int eventAction = event.getAction(); 
      if(eventAction == MotionEvent.ACTION_DOWN){ 
       isLongPress[0] = true; 
       someHandler.postDelayed(someCall, duration); 
      } 
      else if (eventAction == MotionEvent.ACTION_UP) { 
       isLongPress[0] = false; 
       someHandler.removeCallbacks(someCall); 
      } 
      return false; 
     } 
    }); 
+0

'MotionEvent.ACTION_MOVE' se estaba disparando y provocando' removeCallbacks' – UnLoCo

3

Esta es la forma más sencilla que he encontrado para lograr este comportamiento. Tiene un par de ventajas sobre la respuesta actualmente aceptada.

  1. Al marcar view.isPressed nos aseguramos de que la onClick y onLongClick no se activan si el evento táctil sale de la vista. Esto imita el comportamiento predeterminado onClick y onLongClick del sistema.
  2. El evento ya almacena información de temporización por lo que no es necesario almacenar la hora de inicio en ACTION_DOWN o calcular la hora actual en ACTION_UP. Esto significa que podemos usarlo para múltiples vistas al mismo tiempo porque no estamos rastreando la hora de inicio del evento en una sola variable fuera de onTouch.

NOTA: ViewConfiguration.getLongPressTimeout() es el valor predeterminado y puede cambiar esa marca para usar cualquier valor que desee.

NOTA: Si normalmente no se puede hacer clic en la vista, deberá llamar al view.setClickable(true) para que la comprobación view.isPressed() funcione.

@Override 
public boolean onTouch(View view, MotionEvent event) { 
    if (view.isPressed() && event.getAction() == MotionEvent.ACTION_UP) { 
     long eventDuration = event.getEventTime() - event.getDownTime(); 
     if (eventDuration > ViewConfiguration.getLongPressTimeout()) { 
      onLongClick(view); 
     } else { 
      onClick(view); 
     } 
    } 
    return false; 
} 

Si, como @ampersandre, le gustaría que el evento click largo para activar de inmediato una vez que se alcanza el tiempo de retardo en lugar de esperar a que ACTION_UP a continuación las siguientes obras bien para mí.

@Override 
public boolean onTouch(View view, MotionEvent event) { 
    if (event.getAction() == MotionEvent.ACTION_DOWN) { 
     view.setTag(true); 
    } else if (view.isPressed() && (boolean) view.getTag()) { 
     long eventDuration = event.getEventTime() - event.getDownTime(); 
     if (eventDuration > ViewConfiguration.getLongPressTimeout()) { 
      view.setTag(false); 
      onLongClick(view); 
     } else if (event.getAction() == MotionEvent.ACTION_UP) { 
      onClick(view); 
     } 
    } 
    return false; 
} 
+0

¡Hola! Gracias por su respuesta, fue realmente útil para mí. Tengo un pequeño problema con su solución (el evento de clic largo para disparar inmediatamente una vez que se alcanza el período de retraso uno): necesita tener eventos táctiles continuamente para verificar si la duración> el tiempo de espera. Por lo tanto, si U no tiene pequeños movimientos en el botón mientras está presionado (evento de movimiento ...), el disparo deLongClick puede ocurrir más tarde que el tiempo de espera. ¿Tienes alguna sugerencia? ¡Muchas gracias! – Tom

0

Encontré una solución simple para estudiar cómo funciona el evento de pulsación larga. Cada vez que se hace clic en una vista, se agrega un Runnable de tipo CheckForLongPress a una cola con un retraso. Si la demora finaliza, se llama al OnLongClickListener. Si hay un evento diferente antes de que el retraso termine, entonces, el CheckForLongPress Runnable se elimina de la cola.

acabo de reemplazar el método pública postDelayed(Runnable action, long delayMillis) de la vista para cambiar el retraso del sistema operativo

@Override public boolean postDelayed(Runnable action, long delayMillis) { 
    boolean isLongPress = action.getClass().getSimpleName().equals("CheckForLongPress"); 
    return super.postDelayed(action, isLongPress ? LONG_PRESS_MILLIS : delayMillis); 
} 

puse LONG_PRESS_MILLIS a 100 y que está funcionando!

Espero que ayude! ;)

Cuestiones relacionadas