Android 在 EditText 中更改文本后 0.5 秒,我该怎么做?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/12142021/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How can I do something, 0.5 second after text changed in my EditText?
提问by Bobs
I am filtering my list using an EditText. I want to filter the list 0.5 second after user has finished typing in EditText. I used the afterTextChanged
event of TextWatcher
for this purpose. But this event rises for each character changes in EditText.
我正在使用 EditText 过滤我的列表。我想在用户完成 EditText 输入后 0.5 秒过滤列表。为此,我使用了afterTextChanged
事件TextWatcher
。但是这个事件会随着 EditText 中每个字符的变化而上升。
What should I do?
我该怎么办?
回答by Ber?ák
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
);
}
}
);
The trick is in canceling and re-scheduling Timer
each time, when text in EditText
gets changed. Good luck!
诀窍是在Timer
每次EditText
更改文本时取消和重新安排。祝你好运!
UPDATEFor those interested in how long to set the delay, see this post.
更新对于那些对设置延迟多长时间感兴趣的人,请参阅这篇文章。
回答by NazarK
Better use Handler with postDelayed() method. In the android's implementation Timer will create new thread each time to run the task. Handler however has its own Looper that can be attached to whatever thread we wish, so we won't pay extra cost to create thread.
更好地使用 Handler 和 postDelayed() 方法。在android的实现中,Timer每次都会创建新的线程来运行任务。然而,Handler 有自己的 Looper,可以附加到我们想要的任何线程上,所以我们不会为创建线程支付额外的成本。
Example
例子
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) {
//
}
回答by Anton Makhrov
You can use RxBindings, it's the best solution. See guide to RxJava operator debounce, I'm sure that will do great in your case.
您可以使用RxBindings,这是最好的解决方案。请参阅 RxJava 运算符 debounce 指南,我相信这对您的情况会很好。
RxTextView.textChanges(editTextVariableName)
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe(new Action1<String>() {
@Override
public void call(String value) {
// do some work with the updated text
}
});
回答by Beemo
Non of the above solution worked for me.
上述解决方案都不适合我。
I needed a way for TextWatcher to not fire on every character I input inside my search view and show some progress, meaning I need to access UI thread.
我需要一种方法让 TextWatcher 不触发我在搜索视图中输入的每个字符并显示一些进度,这意味着我需要访问 UI 线程。
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) {}
};
Removing Handler on every onTextChanged (which is called when the user inputs a new character). afterTextChanged is called after the text has been changed inside input field where we can start new Runnable, but will cancel it if user types more characters (For more info, when these callback are called, see this). If user doesn't input anymore characters, interval will pass in postDelayed and it will call work you should do with that text.
在每个 onTextChanged 上删除处理程序(当用户输入新字符时调用)。afterTextChanged 在输入字段内更改文本后调用,我们可以在其中启动新的 Runnable,但如果用户键入更多字符,则会取消它(有关更多信息,当这些回调被调用时,请参阅此)。如果用户不再输入字符,interval 将传入 postDelayed 并调用您应该对该文本执行的工作。
This code will run only once per interval, not for every key user inputs. Hope it helps someone in the future.
此代码每个时间间隔仅运行一次,而不是针对每个关键用户输入。希望它可以帮助将来的某个人。
回答by Kyriakos Georgiopoulos
With Kotlin extension functions and coroutines:
使用 Kotlin 扩展函数和协程:
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) {}
})}
回答by pgsandstrom
How do you determine that they have finished writing? That the edittext loses focus? Then there is setOnFocusChangedListener.
你如何确定他们已经完成了写作?编辑文本失去焦点?然后是setOnFocusChangedListener。
Responding to latest edit in question: If you want to wait a specific time after the latest key stroke, then you have to start up a thread at the first keypress (use TextWatcher). Constantly register the time of the latest key stroke. Let the thread sleep to the the time of the latest keystroke + 0.5 seconds. If the timestamp of the latest keystroke has not been updated, do whatever you had intended.
回应有问题的最新编辑:如果您想在最近一次击键后等待特定时间,那么您必须在第一次按键时启动一个线程(使用 TextWatcher)。不断记录最近一次击键的时间。让线程休眠到最近一次击键的时间 + 0.5 秒。如果最新击键的时间戳尚未更新,请按您的意图进行操作。
回答by CommonSenseCode
You can also use TextWatcherinterface and create your custom class that implements it to re-use many times your CustomTextWatcherand also you can pass views or whatever you might need to its constructor:
您还可以使用TextWatcher接口并创建实现它的自定义类以多次重复使用您的CustomTextWatcher并且您还可以将视图或您可能需要的任何内容传递给其构造函数:
public abstract class CustomTextWatcher implements TextWatcher { //Notice abstract class so we leave abstract method textWasChanged() for implementing class to define it
private final TextView myTextView; //Remember EditText is a TextView so this works for EditText also
public AddressTextWatcher(TextView tView) { //Notice I'm passing a view at the constructor, but you can pass other variables or whatever you need
myTextView= tView;
}
private Timer timer = new Timer();
private final int DELAY = 500; //milliseconds of delay for timer
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(final Editable s) {
timer.cancel();
timer = new Timer();
timer.schedule(
new TimerTask() {
@Override
public void run() {
textWasChanged();
}
},
DELAY
);
}
public abstract void textWasChanged(); //Notice abstract method to leave implementation to implementing class
}
Now in your activity you can use it like this:
现在在您的活动中,您可以像这样使用它:
myEditText.addTextChangedListener(new CustomTextWatcher(myEditText) { //Notice I'm passing in constructor of CustomTextWatcher myEditText I needed to use
@Override
public void textWasChanged() {
//doSomething(); this is method inside your activity
}
});
回答by Android Dev
In Kotlin Language you can do like this
在 Kotlin 语言中,您可以这样做
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) {
}
}
回答by Gyan Swaroop Awasthi
You can use timer, after typing the text it will wait for 600 ms. Put the code inside afterTextChanged() by using delay of 600 ms.
您可以使用计时器,输入文本后它将等待 600 毫秒。使用 600 毫秒的延迟将代码放入 afterTextChanged() 中。
@Override
public void afterTextChanged(Editable arg0) {
// user typed: start the timer
timer = new Timer();
timer.schedule(new TimerTask() {
@Override
public void run() {
// do your actual work here
editText.setText(et.getText().toString());
}
}, 600); // 600ms delay before the timer executes the ?run“ method from TimerTask
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// nothing to do here
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// user is typing: reset already started timer (if existing)
if (timer != null) {
timer.cancel();
}
}
};
回答by Boldijar Paul
Try this
尝试这个
class DelayTextWatcher(val ms: Long = 500, val textChanged: (String) -> Unit) : TextWatcher {
private var timer: CountDownTimer? = null
override fun afterTextChanged(p0: Editable) {
timer?.cancel()
timer = object : CountDownTimer(ms, ms) {
override fun onTick(millisUntilFinished: Long) {
}
override fun onFinish() {
textChanged(p0.toString())
}
}.start()
}
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
}
fun dispose() {
timer?.cancel()
}
}
}