Wie kann ich 0,5 Sekunden nach der Textänderung in meinem EditText-Steuerelement etwas tun?
Lesezeit: 6 Minuten
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?
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
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.
@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
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
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) {}
})}
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
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
12568700cookie-checkWie kann ich 0,5 Sekunden nach der Textänderung in meinem EditText-Steuerelement etwas tun?yes