Wie kann ich 0,5 Sekunden nach der Textänderung in meinem EditText-Steuerelement etwas tun?

Lesezeit: 6 Minuten

Benutzer-Avatar
Bobs

Ich filtere meine Liste mit einem EditText-Steuerelement. Ich möchte die Liste filtern 0,5 Sekunden, nachdem der Benutzer mit der Eingabe von EditText fertig ist. Ich habe die verwendet afterTextChanged Veranstaltung von TextWatcher für diesen Zweck. Aber dieses Ereignis steigt für jede Zeichenänderung in EditText.

Was soll ich machen?

Benutzer-Avatar
Berťák

Verwenden:

editText.addTextChangedListener(
    new TextWatcher() {
        @Override public void onTextChanged(CharSequence s, int start, int before, int count) { }
        @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { }

        private Timer timer = new Timer();
        private final long DELAY = 1000; // Milliseconds

        @Override
        public void afterTextChanged(final Editable s) {
            timer.cancel();
            timer = new Timer();
            timer.schedule(
                new TimerTask() {
                    @Override
                    public void run() {
                        // TODO: Do what you need here (refresh list).
                        // You will probably need to use
                        // runOnUiThread(Runnable action) for some
                        // specific actions (e.g., manipulating views).
                    }
                },
                DELAY
            );
        }
    }
);

Der Trick liegt im Stornieren und Umplanen Timer jedes Mal, wenn Text eingeht EditText wird geändert.

Informationen zum Einstellen der Verzögerung finden Sie unter dieser Beitrag.

  • Überprüfen Sie dies einmal, stackoverflow.com/questions/10217051/…

    – Saurabh

    23. Oktober 2013 um 7:30 Uhr

  • Wo ist delayInMillis definiert? Kann das irgendwie weitergegeben werden?

    – Gonzohirne

    22. Mai 2014 um 19:07 Uhr

  • Sicher, delayInMillis ist beliebig long Variable, die die Verzögerung darstellt (in diesem Fall 0,5 s). Sie können auch verwenden 500 stattdessen. Siehe meine aktualisierte Antwort.

    – Berťák

    23. Mai 2014 um 8:29 Uhr


  • Für diejenigen, die runonUiThread ausführen möchten … ((Activity)getContext()).runOnUiThread() oder getActivity().runOnUiThread()

    – Ken Ratanachai S.

    6. März 2017 um 6:23 Uhr


  • Wenn der Code der TimerTask groß ist, wäre es besser, ihn nicht jedes Mal zu erstellen (weil der Zeitplan bei jedem Tastendruck aufgerufen wird), sondern ein Objekt wiederzuverwenden? Ich habe auch versucht, die Zeit nicht erneut zu erstellen, aber es schien, dass ein einmal abgebrochener Timer nicht für einen neuen Zeitplan wiederverwendet werden kann.

    – Verdammtes Gemüse

    19. Januar 2018 um 20:50 Uhr

Benutzer-Avatar
NazarK

Besser verwenden Handler mit der Methode postDelayed(). In der Android-Implementierung erstellt Timer jedes Mal einen neuen Thread, um die Aufgabe auszuführen. Handlerhat jedoch einen eigenen Looper, der an jeden gewünschten Thread angehängt werden kann, sodass wir keine zusätzlichen Kosten für die Erstellung eines Threads zahlen.

Beispiel

 Handler handler = new Handler(Looper.getMainLooper() /*UI thread*/);
 Runnable workRunnable;
 @Override public void afterTextChanged(Editable s) {
    handler.removeCallbacks(workRunnable);
    workRunnable = () -> doSmth(s.toString());
    handler.postDelayed(workRunnable, 500 /*delay*/);
 }

 private final void doSmth(String str) {
    //
 }

  • Warum müssen Sie die Rückrufe entfernen?

    – Der Lernende

    2. März 2018 um 2:37 Uhr

  • @TheLearner Damit es nicht mehrmals aufgerufen wird. Wenn Sie den vorherigen nicht abbrechen, wird er trotzdem ausgeführt. Das bringt uns zurück zum eigentlichen Punkt dieser Frage, um sie zu entprellen.

    – Deny

    21. August 2018 um 18:08 Uhr


  • Ich weiß, dass dies eine alte Antwort ist, aber was denkst du passiert, wenn du das geplant hast Runnable nach einer Weile aufgerufen werden und der Kontext entfernt wird, weil sich der Benutzer beispielsweise von diesem Bildschirm entfernt?

    – Darwind

    7. September 2018 um 8:25 Uhr

  • handler.removeCallbacks(workRunnable); zusätzlich hinzugezogen werden onPause von Fragment/Aktivität, um die Ausführung zu verhindern, wenn die Benutzeroberfläche nicht angezeigt wird.

    – MatPag

    15. Januar 2021 um 9:12 Uhr

Benutzer-Avatar
Anton Machrow

Sie können verwenden RxBindings; es ist die beste lösung. Siehe die Anleitung zum RxJava-Operator entprellen. Ich bin sicher, das wird in Ihrem Fall großartig tun.

RxTextView.textChanges(editTextVariableName)
            .debounce(500, TimeUnit.MILLISECONDS)
            .subscribe(new Action1<String>() {
                @Override
                public void call(String value) {
                    // Do some work with the updated text
                }
            });

  • Sie werden wahrscheinlich auch brauchen .observeOn(AndroidSchedulers.mainThread()) damit das funktioniert

    – Isen Ng

    22. Oktober 2021 um 8:38 Uhr

Benutzer-Avatar
Kyriakos Georgiopoulos

Mit Kotlin-Erweiterungsfunktionen und Coroutinen:

fun AppCompatEditText.afterTextChangedDebounce(delayMillis: Long, input: (String) -> Unit) {
    var lastInput = ""
    var debounceJob: Job? = null
    val uiScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
    this.addTextChangedListener(object : TextWatcher {
        override fun afterTextChanged(editable: Editable?) {
            if (editable != null) {
                val newtInput = editable.toString()
                debounceJob?.cancel()
                if (lastInput != newtInput) {
                    lastInput = newtInput
                    debounceJob = uiScope.launch {
                        delay(delayMillis)
                        if (lastInput == newtInput) {
                            input(newtInput)
                        }
                    }
                }
            }
        }

        override fun beforeTextChanged(cs: CharSequence?, start: Int, count: Int, after: Int) {}
        override fun onTextChanged(cs: CharSequence?, start: Int, before: Int, count: Int) {}
})}

Benutzer-Avatar
Beemo

Keine der oben genannten Lösungen hat bei mir funktioniert.

Ich brauchte eine Möglichkeit für TextWatcher, nicht bei jedem Zeichen zu feuern, das ich in meine Suchansicht eingebe, und einen Fortschritt anzuzeigen, was bedeutet, dass ich auf den UI-Thread zugreifen muss.

private final TextWatcher textWatcherSearchListener = new TextWatcher() {
    final android.os.Handler handler = new android.os.Handler();
    Runnable runnable;

    public void onTextChanged(final CharSequence s, int start, final int before, int count) {
        handler.removeCallbacks(runnable);
    }

    @Override
    public void afterTextChanged(final Editable s) {
        // Show some progress, because you can access UI here
        runnable = new Runnable() {
            @Override
            public void run() {
                // Do some work with s.toString()
            }
        };
        handler.postDelayed(runnable, 500);
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
};

Entfernen des Handlers bei jedem onTextChanged (der aufgerufen wird, wenn der Benutzer ein neues Zeichen eingibt). afterTextChanged wird aufgerufen, nachdem der Text im Eingabefeld geändert wurde, wo wir ein neues Runnable starten können, aber es wird abgebrochen, wenn der Benutzer mehr Zeichen eingibt (für weitere Informationen, wenn diese Rückrufe aufgerufen werden, siehe hier). Wenn der Benutzer keine weiteren Zeichen eingibt, wird das Intervall in postDelayed übergeben und die Arbeit aufgerufen, die Sie mit diesem Text erledigen sollten.

Dieser Code wird nur einmal pro Intervall ausgeführt, nicht für jede wichtige Benutzereingabe.

  • Können Sie erklären, warum wir den Rückruf entfernen müssen und wie der Handler in diesem Fall funktioniert? Steht das im Hauptthread? Versuche zu verstehen, wie das alles funktioniert.

    – Der Lernende

    2. März 2018 um 3:08 Uhr

Benutzer-Avatar
Peter Mortensen

In der Kotlin-Sprache können Sie dies folgendermaßen tun:

tv_search.addTextChangedListener(mTextWatcher)

private val mTextWatcher: TextWatcher = object : TextWatcher {
    private var timer = Timer()
    private val DELAY: Long = 1000L

    override fun afterTextChanged(s: Editable?) {
        timer.cancel()
        timer = Timer()
        timer.schedule(object : TimerTask() {
            override fun run() {

                // Do your stuff here
            }
        }, DELAY)
    }

    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
    }

    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
    }

}

  • Können Sie erklären, warum wir den Rückruf entfernen müssen und wie der Handler in diesem Fall funktioniert? Steht das im Hauptthread? Versuche zu verstehen, wie das alles funktioniert.

    – Der Lernende

    2. März 2018 um 3:08 Uhr

Wie stellen Sie fest, dass sie mit dem Schreiben fertig sind? Dass der Bearbeitungstext den Fokus verliert? Dann ist da setOnFocusChangedListener.

Antwort auf die letzte Änderung in Frage: Wenn Sie nach dem letzten Tastendruck eine bestimmte Zeit warten möchten, müssen Sie beim ersten Tastendruck einen Thread starten (verwenden Sie TextWatcher). Registrieren Sie ständig die Zeit des letzten Tastendrucks. Lassen Sie den Thread bis zum Zeitpunkt des letzten Tastendrucks + 0,5 Sekunden schlafen. Wenn der Zeitstempel des letzten Tastendrucks nicht aktualisiert wurde, tun Sie, was Sie beabsichtigt hatten.

  • 0,5 Sekunden, nachdem der Benutzer mit der Eingabe fertig ist. Wie kann ich das machen?

    – Bubi

    27. August 2012 um 12:50 Uhr


1256870cookie-checkWie kann ich 0,5 Sekunden nach der Textänderung in meinem EditText-Steuerelement etwas tun?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy