如何在 EditText 控件中的文本更改后 0.5 秒执行某些操作?

问题描述 投票:0回答:18

我正在使用 EditText 控件过滤我的列表。我想在用户完成 EditText 的输入后 0.5 秒过滤列表。为此,我使用了 afterTextChanged

TextWatcher
事件。但是这个事件会随着 EditText 中每个字符的变化而发生。
我该怎么办?

android filter android-edittext textwatcher textchanged
18个回答
173
投票

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

诀窍在于每次 
Timer

中的文本发生更改时取消并重新安排

EditText
设置延迟多长时间,请参阅

这篇文章


56
投票
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) { // }



17
投票
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 } });



17
投票

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



13
投票

我需要一种方法,让 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 上删除处理程序(当用户输入新字符时调用)。 afterTextChanged 在输入字段内的文本更改后被调用,我们可以在其中启动一个新的 Runnable,但如果用户输入更多字符,它将取消它(有关更多信息,当调用这些回调时,
参见 this

)。如果用户不再输入任何字符,间隔将在 postDelayed 中传递,它将调用您应该对该文本执行的工作。 此代码每个时间间隔仅运行一次,而不是针对每个关键用户输入。


7
投票

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



5
投票

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

创建一个 Watcher 类以在更改后提供文本

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



4
投票
setOnFocusChangedListener

响应最新编辑问题

:如果您想在最近一次按键后等待特定时间,那么您必须在第一次按键时启动一个线程(使用 TextWatcher)。不断记录最近一次击键的时间。让线程休眠到最近一次击键的时间 + 0.5 秒。如果最近一次击键的时间戳尚未更新,请执行您想要的操作。


3
投票
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 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 } });



2
投票

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



0
投票

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

}


0
投票

if (charSequence.length() > 0){ // Your code }



0
投票

这将允许 textWatcher 从第二次开始进行任何更改。

Boolean firstchange = false; profileEmailEditText.addTextChangedListener(new TextWatcher() { @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void onTextChanged(CharSequence s, int start, int before, int count) { if (firstchange) { emailAlertText.setVisibility(View.VISIBLE); } else { firstchange = true; } } @Override public void afterTextChanged(Editable s) { } });



0
投票

此方法使用协程而不是线程(如果您将通过 Timer() 执行此操作)。此外,您可以通过 launchWhenCreated 等控制

debounceJob

的生命周期

private val onNumberListener = object : TextWatcher {
    private var debounceJob: Job? = null
    private val DELAY: Long = 1000L

    override fun afterTextChanged(s: Editable?) {
        debounceJob?.cancel()
        debounceJob = [email protected]
            .launch(Dispatchers.Main) {
                delay(DELAY)
                viewModel.onNumberChange(s?.toString() ?: "")
            }
    }

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

    }

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

    }
}



0
投票

setSelection(it.toString().length)

使用这种形式,不要使用树或协程来睡眠N tine


0
投票

searchProductsViewModel.viewModelScope.launch { delay(500) searchProductsViewModel.products() }



-1
投票
Timer文档

,最好使用ScheduledThreadPoolExecutor -

“计时器安排一次性或重复性任务的执行。更喜欢 新代码的 ScheduledThreadPoolExecutor。”

这是一个更好的方法

Runnable runnabledelayedTask = new Runnable(){ @Override public void run(){ //TODO Perform any operation here } }; 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 final long DELAY = 500; // Milliseconds @Override public void afterTextChanged(final Editable s) { ScheduledExecutorService scheduledPool = Executors.newScheduledThreadPool(1); ScheduledFuture sf = scheduledPool.schedule(callabledelayedTask, DELAY, TimeUnit.MILLISECONDS); // You can cancel ScheduledFuture when needed } } );



-2
投票

EditorActionListener

 来实现此目的。
editText.setOnEditorActionListener(new TextView.OnEditorActionListener() { @Override public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if (actionId == EditorInfo.IME_ACTION_DONE) { //Do something here return true; } return false; } });

© www.soinside.com 2019 - 2024. All rights reserved.