21 Stimmen

Das LongClick-Ereignis tritt zu schnell auf. Wie kann ich die erforderliche Klickzeit erhöhen, um es auszulösen?

In einer Anwendung, an der ich arbeite, habe ich die Anforderung, dass ein Benutzer eine Komponente für eine bestimmte Zeit gedrückt halten muss, bevor eine bestimmte Aktion ausgelöst wird.

Derzeit verwende ich einen OnLongClickListener, um auf den Langklick zu hören, aber ich finde, dass die Dauer eines Klicks, um das OnLongClick-Ereignis auszulösen, zu kurz ist.

Zum Beispiel, sagen wir, das LongClick-Ereignis wird nach einem 400ms Klick ausgelöst, ich möchte aber, dass der Benutzer für 1200ms klicken und halten muss, bevor das Ereignis ausgelöst wird.

Gibt es eine Möglichkeit, das LongClick-Ereignis so zu konfigurieren, dass ein längerer Klick erforderlich ist?
Oder gibt es vielleicht eine andere Konstruktion, die es mir ermöglichen würde, auf längere Klicks zu hören?

21voto

Rohan Punkte 7976

Es ist nicht möglich, den Timer beim onLongClick-Ereignis zu ändern, er wird vom Android selbst verwaltet.

Was möglich ist, ist die Verwendung von .setOnTouchListener().

Registrieren Sie dann, wenn das MotionEvent ein ACTION_DOWN ist.
Notieren Sie die aktuelle Zeit in einer Variablen.
Dann, wenn ein MotionEvent mit ACTION_UP registriert wird und die current_time - actionDown-Zeit > 1200 ms, dann tun Sie etwas.

also ziemlich:

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

6voto

mpkuth Punkte 6809

Dies ist der einfachste Weg, den ich gefunden habe, um dieses Verhalten zu erreichen. Es hat einige Vorteile gegenüber der derzeit akzeptierten Antwort.

  1. Indem wir view.isPressed überprüfen, stellen wir sicher, dass das onClick und das onLongClick nicht ausgelöst werden, wenn das Berühren des Elements endet. Dies ahmt das Standardverhalten von onClick und onLongClick des Systems nach.
  2. Das Ereignis speichert bereits zeitliche Informationen, sodass es nicht erforderlich ist, die Startzeit bei ACTION_DOWN zu speichern oder die aktuelle Zeit bei ACTION_UP selbst zu berechnen. Das bedeutet, dass wir es für mehrere Ansichten gleichzeitig verwenden können, da wir die Ereignisstartzeit außerhalb von onTouch nicht in einer einzigen Variablen verfolgen.

HINWEIS: ViewConfiguration.getLongPressTimeout() ist der Standardwert und Sie können diese Überprüfung auf jeden beliebigen Wert ändern.

HINWEIS: Wenn die Ansicht normalerweise nicht anklickbar ist, müssen Sie view.setClickable(true) aufrufen, damit die Überprüfung von view.isPressed() funktioniert.

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

Wenn Sie, wie @ampersandre, möchten, dass das Long-Click-Ereignis sofort ausgelöst wird, sobald die Verzögerungszeit erreicht ist, anstatt auf ACTION_UP zu warten, funktioniert folgendes gut für mich.

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

3voto

ampersandre Punkte 3046

Ich habe mit Hilfe von Rohan eine Lösung gefunden :)
Ich habe seine Antwort angepasst, um meinen Anforderungen zu entsprechen.

Wenn der Benutzer die Taste drückt, wird ein Thread gestartet. Der Thread schläft für die gewünschte Verzögerung, und wenn er aufwacht, führt er den Code aus, den ich brauche. Wenn der Benutzer loslässt, wird der Thread beendet. Dadurch wird erreicht, was ich will, denn wenn der Benutzer loslässt, bevor der Thread aufwacht, wird der Thread unterbrochen und die Aktion findet nicht statt.

Ich mag diesen Ansatz, weil er es mir ermöglicht, meine Geschäftslogik sofort nach Ablauf der Verzögerung auszuführen, was gut ist, weil ich dem Benutzer ein Feedback geben kann, um ihm zu zeigen, dass er lange genug gedrückt hat (zum Beispiel durch Vibration des Telefons).
Der Nachteil dieses Ansatzes ist: Es besteht das Risiko, dass der Benutzer die Taste loslässt, während Ihre gewünschte Aktion läuft, und den Thread beendet, bevor alles erledigt ist. Dies ist für meinen Fall kein großes Problem, weil meine Geschäftslogik sehr wenig macht; sie löst nur ein Ereignis aus, das von einer anderen Klasse verarbeitet wird. Wenn die Aktion nicht vollständig abgeschlossen wurde, ist es akzeptabel, dass der Benutzer es erneut versuchen muss.

Der Code ist etwas länger als ich möchte, aber wenn dies ein häufiges Merkmal in Ihrer Anwendung ist, ist er leicht wiederverwendbar. Hier ist ein Codebeispiel:

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

    public boolean onTouch(View view, MotionEvent event) {
        // Wenn der Benutzer drückt und es keinen Thread gibt, erstellen Sie einen und starten ihn
        if (event.getAction() == MotionEvent.ACTION_DOWN && longClickSensor == null) {
            longClickSensor = new Thread(new MyDelayedAction());
            longClickSensor.start();
        }
        // Wenn der Benutzer loslässt und es einen Thread gab, stoppen Sie ihn und vergessen Sie den 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); // Eine Weile schlafen
                doBusinessLogic();     // Wenn der Thread nach dem Schlafen noch aktiv ist, die Arbeit erledigen
            } catch (InterruptedException e) { return; }
        }
        private void doBusinessLogic() {
            // Stellen Sie sicher, dass diese Logik so schnell wie möglich ist, oder delegieren Sie sie an eine andere Klasse
            // durch Broadcasted Intents, denn wenn der Benutzer loslässt, während die Arbeit läuft,
            // wird der Thread unterbrochen.
        }
    }
}

3voto

MartinCR Punkte 658

Ich habe eine einfache Lösung gefunden, indem ich untersucht habe, wie das Ereignis für lange Berührung funktioniert. Jedes Mal, wenn eine Ansicht angeklickt wird, wird ein Runnable vom Typ CheckForLongPress mit einer Verzögerung in eine Warteschlange eingefügt. Wenn die Verzögerung abläuft, wird der OnLongClickListener aufgerufen. Wenn es ein anderes Ereignis vor Ablauf der Verzögerung gibt, wird der CheckForLongPress Runnable aus der Warteschlange entfernt.

Ich überschreibe einfach die öffentliche Methode postDelayed(Runnable action, long delayMillis) der Ansicht, um die Verzögerung des Betriebssystems zu ändern

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

Ich setze LONG_PRESS_MILLIS auf 100 und es funktioniert!

Ich hoffe, das hilft!!! ;)

2voto

Fadel Sabbagh Punkte 185
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]) {
                // Ihr Code kommt hierhin
            }
        }
    };

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X