2011-10-28 8 views
16

W aplikacji, nad którą pracuję, mam wymóg, aby użytkownik kliknął & przytrzymaj komponent przez pewien czas, zanim nastąpi określone działanie.Wydarzenie LongClick dzieje się zbyt szybko. Jak mogę zwiększyć czas clicktime wymagany do uruchomienia?

Aktualnie korzystam z OnLongClickListener do nasłuchiwania longclick, ale uważam, że długość kliknięcia, które ma wywołać zdarzenie OnLongClick, jest zbyt krótka.

Załóżmy na przykład, że zdarzenie LongClick uruchamia się po 400 ms kliknięcia, ale chcę, aby użytkownik musiał kliknąć & przytrzymaj przez 1200 ms przed wyzwalaniem zdarzenia.

Czy istnieje sposób, w jaki mogę skonfigurować zdarzenie LongClick, aby wymagało dłuższego kliknięcia?
A może jest inny konstrukt, który pozwoli mi słuchać dłuższych kliknięć?

Odpowiedz

17

Nie można zmienić licznika czasu w zdarzeniu onLongClick, zarządza nim sam Android.

Możliwe jest użycie .setOnTouchListener().

Następnie zarejestruj się, gdy MotionEvent jest ACTION_DOWN.
Zwróć uwagę na bieżący czas w zmiennej.
Następnie, gdy zarejestrowany jest MotionEvent z ACTION_UP i current_time - actionDown czas> 1200 ms, to zrób coś.

więc dość dużo:

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

To prawie robi to, czego potrzebuję. Wpisane podejście wykonuje kod tylko wtedy, gdy użytkownik podnosi palec. Chciałbym, aby moja logika biznesowa działała zaraz po opóźnieniu, a nie po tym, jak użytkownik przestanie naciskać. – ampersandre

+0

Dlaczego jest następnie = getSystemTime() skomentowane? Również metoda getSystemTime() nie działa. Czy miałeś na myśli: System.currentTimeMillis() –

+0

Potwierdzono. Zdefiniuj więc jako długą, nie int. Następnie użyj: then = (Long) System.currentTimeMillis(); a kiedy odnajdziemy różnicę, upewnijmy się, że ponownie rzucamy na długi czas, gdy używamy System.currentTimeMillis() zamiast getSystemTime() –

3

I wypracowane rozwiązanie z pomocą Rohan :)
I dostosowany jego odpowiedź, aby dopasować moje wymagania.

Gdy użytkownik naciśnie przycisk, rozpoczyna się wątek. Wątek przesypia moje pożądane opóźnienie, a kiedy się budzi, wykonuje kod, który jest mu potrzebny. Kiedy użytkownik puści, wątek zostanie zabity. Osiąga to, co chcę, ponieważ jeśli użytkownik puści, zanim wątek się obudzi, wątek zostanie przerwany, a akcja nie zostanie wykonana.

Podoba mi się to podejście, ponieważ pozwala mi wykonywać swoją logikę biznesową, gdy tylko opóźnienie się skończy, co jest dobre, ponieważ mogę dać użytkownikowi pewne informacje zwrotne, dając do zrozumienia, że ​​pchają wystarczająco długo (telefon może wibrować, na przykład).
Wadą tego podejścia jest: istnieje ryzyko, że użytkownik puści przycisk, gdy pożądane działanie zostanie uruchomione, i zabije wątek, zanim wszystko zostanie zrobione. Nie stanowi to wielkiego problemu w mojej sprawie, ponieważ moja logika biznesowa jest bardzo niewielka; po prostu wystrzeliwuje wydarzenie dla jakiejś innej klasy do przetworzenia. Jeśli działanie nie zakończyło się w pełni, możliwe jest, że użytkownik będzie musiał spróbować ponownie.

Kod jest nieco dłuższy, niż by mi się spodobał, ale jeśli jest to typowa funkcja w aplikacji, można ją łatwo ponownie wykorzystać. Oto przykładowy kod:

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

To stanie się kłopotliwe, gdy użytkownik użyje drugiego palca. powinieneś sprawdzić, czy już został rozpoczęty wątek, lub powinieneś śledzić różne typy pickerów. – 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' strzelał i powodował' removeCallbacks' – UnLoCo

3

Jest to najprostszy sposób, że znalazłem się osiągnąć tego zachowania. Ma kilka zalet w porównaniu z obecnie akceptowaną odpowiedzią.

  1. Zaznaczając view.isPressed mamy pewność, że onClick i onLongClick nie są uruchamiane, jeśli zdarzenie dotykowy pozostawia widoku. To naśladuje domyślne zachowanie systemu w postaci onClick i onLongClick.
  2. Zdarzenie już przechowuje informacje o taktowaniu, więc nie ma potrzeby zapisywania czasu rozpoczęcia na ACTION_DOWN lub obliczania bieżącego czasu na ACTION_UP sami. Oznacza to, że możemy go używać do wielu widoków jednocześnie, ponieważ nie śledzimy czasu rozpoczęcia zdarzenia w pojedynczej zmiennej poza onTouch.

UWAGA: ViewConfiguration.getLongPressTimeout() jest ustawieniem domyślnym i można go zmienić, aby użyć dowolnej wartości.

UWAGA: Jeśli widok normalnie nie jest klikalny, należy zadzwonić pod numer view.setClickable(true), aby sprawdzić, czy działa view.isPressed().

@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; 
} 

Jeśli, jak @ampersandre, chciałby długiej zdarzenie click natychmiast wywołać gdy okres opóźnienia osiągnięciu zamiast czekać na ACTION_UP wówczas następujące prace dobrze dla mnie.

@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

Hi! Dzięki za odpowiedź, było to dla mnie bardzo pomocne. Mam mały problem z twoim rozwiązaniem (zdarzenie długiego kliknięcia, które uruchamia się natychmiast po osiągnięciu czasu opóźnienia): musi ono mieć ciągłe zdarzenia dotykowe, aby sprawdzić, czy czas trwania> czasowy. Tak więc, jeśli U nie ma małych ruchów na przycisku podczas jego naciśnięcia (ruch zdarzenia ...), wyzwalanie LongLick może nastąpić później niż timon. Czy masz jakieś sugestie? Wielkie dzięki! – Tom

0

Znalazłem proste rozwiązanie sprawdzające, jak działa długie wydarzenie prasowe. Po każdym kliknięciu widoku, do kolejki z opóźnieniem dodaje się Runnable typu CheckForLongPress. Jeśli opóźnienie się zakończy, wywoływana jest nazwa OnLongClickListener. Jeśli istnieje inne zdarzenie przed zakończeniem opóźnienia, to CheckForLongPress Runnable jest usuwany z kolejki de.

po prostu zastąpić metodę publiczną postDelayed(Runnable action, long delayMillis) widoku zmienić opóźnienie OS

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

ustawić LONG_PRESS_MILLIS do 100 i to działa!

Mam nadzieję, że pomoże! ;)

Powiązane problemy