Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/212.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 在EditText控件中更改文本0.5秒后,如何执行操作?_Android_Filter_Android Edittext_Textwatcher_Textchanged - Fatal编程技术网

Android 在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秒。如果最近一次击键的时间戳尚

我正在使用EditText控件筛选列表。我想在用户完成EditText键入后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,这样它就不会被调用多次。如果不取消上一个,它仍将被执行。这让我们回到这个问题的整个要点,来消除它的影响