我正在使用EditText控件过滤我的列表。我希望在用户完成在EditText中输入后0.5秒后再过滤列表。我使用了TextWatcher的afterTextChanged事件来实现这个目的。但是这个事件会在EditText中每次字符改变时触发。
我该怎么办?
我正在使用EditText控件过滤我的列表。我希望在用户完成在EditText中输入后0.5秒后再过滤列表。我使用了TextWatcher的afterTextChanged事件来实现这个目的。但是这个事件会在EditText中每次字符改变时触发。
我该怎么办?
使用:
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
);
}
}
);
每次文本框EditText
中的文本发生变化时,关键是取消和重新安排Timer
。
要设置延迟多长时间,请参见此帖子。
最好使用Handler与postDelayed()方法。在Android的实现中,Timer每次运行任务都会创建一个新线程。Handler有自己的Looper,可以附加到我们希望的任何线程,因此我们不需要额外支付创建线程的成本。
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) {
//
}
Runnable
在一段时间后进行调用,并且上下文被删除,例如因为用户离开了此屏幕,您认为会发生什么? - DarwindonPause
中应该另外调用handler.removeCallbacks(workRunnable);
以防止在UI不可见时运行。 - MatPag你可以使用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
}
});
.observeOn(AndroidSchedulers.mainThread())
才能使其正常工作。 - Isen Ng使用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) {}
})}
以上解决方法对我都没用。
我需要一种方法让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) {}
};
在每次onTextChanged(当用户输入新字符时调用)中删除Handler。afterTextChanged在输入字段内的文本已更改后被调用,我们可以在其中启动一个新的Runnable,但如果用户键入更多字符,则会取消它(有关这些回调何时被调用的更多信息,请参见此处)。如果用户不再输入任何字符,则该时间间隔将在postDelayed中过去,并且它将使用该文本调用应进行的工作。
此代码每个间隔只运行一次,而不是为每个用户输入的按键运行。
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) {
}
}
Timer
实例化都会生成一个后台线程,这种方式的效率如何? - YaMiN观察文本变化事件的另一种方式是使用协程通道。
lifecycleScope.launchWhenCreated {
editText.afterTextChanged {
// do something
}
}
suspend fun EditText.afterTextChanged(afterTextChanged: suspend (String) -> Unit) {
val watcher = Watcher()
this.addTextChangedListener(watcher)
watcher.asFlow()
.debounce(500)
.collect { afterTextChanged(it) }
}
class Watcher : TextWatcher {
private val channel = ConflatedBroadcastChannel<String>()
override fun afterTextChanged(editable: Editable?) {
channel.offer(editable.toString())
}
override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) {}
override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) {}
fun asFlow(): Flow<String> {
return channel.asFlow()
}
}
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 the abstract method to leave the
// implementation to the implementing class
}
// Notice I'm passing in the constructor of CustomTextWatcher
// myEditText I needed to use
myEditText.addTextChangedListener(new CustomTextWatcher(myEditText) {
@Override
public void textWasChanged() {
//doSomething(); This is method inside your activity
}
});
@Override
public void afterTextChanged(Editable arg0) {
// The 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); // 600 ms 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) {
// The user is typing: reset already started timer (if existing)
if (timer != null) {
timer.cancel();
}
}
};
delayInMillis
是任何代表延迟的long
变量(在这种情况下为0.5秒)。您也可以使用500
代替它。请参阅我的更新答案。 - Berťák