Android 在EditText控件中更改文本0.5秒后,如何执行操作?
我正在使用EditText控件筛选列表。我想在用户完成EditText键入后0.5秒过滤列表。为此,我使用了Android 在EditText控件中更改文本0.5秒后,如何执行操作?,android,filter,android-edittext,textwatcher,textchanged,Android,Filter,Android Edittext,Textwatcher,Textchanged,我正在使用EditText控件筛选列表。我想在用户完成EditText键入后0.5秒过滤列表。为此,我使用了TextWatcher的extextchanged事件。但当EditText中的每个字符发生更改时,此事件都会发生 我该怎么办?你如何确定他们已经写完了?文本失去焦点?还有 对有问题的最新编辑的响应:如果您想在最新的按键后等待特定时间,则必须在第一次按键时启动线程(使用TextWatcher)。不断记录最近一次按键的时间。让线程休眠到最近一次击键的时间+0.5秒。如果最近一次击键的时间戳尚
TextWatcher
的extextchanged
事件。但当EditText中的每个字符发生更改时,此事件都会发生
我该怎么办?你如何确定他们已经写完了?文本失去焦点?还有 对有问题的最新编辑的响应:如果您想在最新的按键后等待特定时间,则必须在第一次按键时启动线程(使用TextWatcher)。不断记录最近一次按键的时间。让线程休眠到最近一次击键的时间+0.5秒。如果最近一次击键的时间戳尚未更新,请按照您的意愿执行操作 您可以为此目的使用
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;
}
});
使用:
诀窍在于每当EditText
中的文本发生更改时,取消并重新安排计时器
有关设置延迟的时间,请参阅。您还可以使用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
}
});
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) {
}
}
为您的案例使用计时器并不是最好的解决方案,因为每次都会创建一个新对象。根据,最好使用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
}
}
);
最好将处理程序与postdayed()方法结合使用。在Android的实现中,Timer每次都会创建一个新线程来运行任务。然而,处理程序有自己的循环器,可以连接到我们想要的任何线程,所以我们不会为创建线程支付额外费用
例子
上述解决方案对我都不起作用
我需要一种方法,让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) {}
};
删除每个ContextChanged(当用户输入新字符时调用)上的处理程序。在输入字段中的文本被更改后,我们可以在输入字段中启动一个新的Runnable,但如果用户键入更多字符(有关更多信息,当调用这些回调时,),它将取消该操作。如果用户不再输入任何字符,间隔将传入postDelayed,它将调用您应该处理该文本的工作
此代码每间隔仅运行一次,而不是针对每个关键用户输入。您可以使用;这是最好的解决办法。请参阅RxJava操作符指南。我相信这对你来说会很好
RxTextView.textChanges(editTextVariableName)
.debounce(500, TimeUnit.MILLISECONDS)
.subscribe(new Action1<String>() {
@Override
public void call(String value) {
// Do some work with the updated text
}
});
RxTextView.textChanges(editTextVariableName)
.debounce(500,时间单位毫秒)
.订阅(新操作1(){
@凌驾
公共无效调用(字符串值){
//对更新后的文本进行一些处理
}
});
这是在完成键入时和之后发生的事件。。。添加textWatcher,并在ContextChanged方法中放入:
if (charSequence.length() > 0){
// Your code
}
如果只想第一次跳过textWatcher,请添加以下代码:
这将允许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) {
}
});
在Kotlin语言中,您可以这样做:
// 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
}
});
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) {
}
}
使用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) {}
})}
你可以用定时器。键入文本后,它将等待600毫秒。使用600毫秒的延迟将代码放入PostTextChanged()中
@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();
}
}
};
试试这个
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()
}
}如果你用Kotlin写,你可以这样做
这种方法使用协程而不是线程(如果您将通过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 = this@FragmentName.viewLifecycleOwner.lifecycle.coroutineScope
.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) {
}
}
观察文本更改事件的另一种方法是使用协程通道
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()
}
}
类观察者:TextWatcher{
私有val通道=合并的DBroadcastChannel()
覆盖后文本更改(可编辑:可编辑?){
channel.offer(可编辑的.toString())
}
重写fun beforeTextChanged(s:CharSequence,start:Int,count:Int,after:Int){}
重写fun-onTextChanged(s:CharSequence,start:Int,before:Int,count:Int){}
fun asFlow():Flow{
返回通道。asFlow()
}
}
最好的方法是移动光标
setSelection(it.toString().length)
使用此表单时,不要使用tree或Corroutine睡眠时间当用户按下“回车”或“下一步”按钮时使用此选项。用户完成键入后0.5秒。我该怎么做?检查一下,delayInMillis的定义在哪里?它可以以某种方式传递吗?当然,delayInMillis
是表示延迟的任何long
变量(在本例中为0.5s)。您也可以使用500
来代替它。请参阅我的更新答案。对于那些想阅读的人。。。((活动)getContext()).runOnUiThread()或getActivity().runOnUiThread()如果TimerTask的代码很大,是否最好不要每次都创建它(因为每次击键时都会调用schedule),而是重用一个对象?我也尝试过不再创建时间,但当计时器被取消时,它似乎无法重新用于新的计划。我是否需要为任何UI更改/更新调用RunUnuithRead inside textWasChanged方法?为什么需要删除回调?@TheLearner,这样它就不会被调用多次。如果不取消上一个,它仍将被执行。这让我们回到这个问题的整个要点,来消除它的影响