[英]How to implement AutoCompleteTextView within onQueryTextChange function of SearchView in actionbar
[英]Throttle onQueryTextChange in SearchView
“节流” onQueryTextChange
的最佳方法是什么,以便我的performSearch()
方法每秒只调用一次,而不是每次用户键入时调用?
public boolean onQueryTextChange(final String newText) {
if (newText.length() > 3) {
// throttle to call performSearch once every second
performSearch(nextText);
}
return false;
}
如果您使用 Kotlin 和协程,您可以执行以下操作:
var queryTextChangedJob: Job? = null
...
fun onQueryTextChange(query: String) {
queryTextChangedJob?.cancel()
queryTextChangedJob = launch(Dispatchers.Main) {
delay(500)
performSearch(query)
}
}
基于 aherrick 的代码,我有一个更好的解决方案。 每次更改查询文本时,声明一个可运行变量并清除处理程序上的回调队列,而不是使用布尔值“canRun”。 这是我最终使用的代码:
@Override
public boolean onQueryTextChange(final String newText) {
searchText = newText;
// Remove all previous callbacks.
handler.removeCallbacks(runnable);
runnable = new Runnable() {
@Override
public void run() {
// Your code here.
}
};
handler.postDelayed(runnable, 500);
return false;
}
我想出了一个使用RxJava的解决方案,特别是它的debounce运算符。
使用 Jake Wharton 方便的RxBinding ,我们将拥有:
RxSearchView.queryTextChanges(searchView)
.debounce(1, TimeUnit.SECONDS) // stream will go down after 1 second inactivity of user
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Consumer<CharSequence>() {
@Override
public void accept(@NonNull CharSequence charSequence) throws Exception {
// perform necessary operation with `charSequence`
}
});
创建抽象类:
public abstract class DelayedOnQueryTextListener implements SearchView.OnQueryTextListener { private Handler handler = new Handler(); private Runnable runnable; @Override public boolean onQueryTextSubmit(String s) { return false; } @Override public boolean onQueryTextChange(String s) { handler.removeCallbacks(runnable); runnable = () -> onDelayerQueryTextChange(s); handler.postDelayed(runnable, 400); return true; } public abstract void onDelayedQueryTextChange(String query);
}
像这样设置它:
searchView.setOnQueryTextListener(new DelayedOnQueryTextListener() { @Override public void onDelayedQueryTextChange(String query) { // Handle query } });
对于科林
在coroutineScope.launch(Dispatchers.Main) {}
的情况下,您可能会遇到问题Suspend function '...' should be called only from a coroutine or another suspend function
。
我找到了以下方法
private var queryTextChangedJob: Job? = null
private lateinit var searchText: String
接下来不要忘记使用implementation "androidx.lifecycle:lifecycle-runtime-ktx:2.3.0-alpha05"
override fun onQueryTextChange(newText: String?): Boolean {
val text = newText ?: return false
searchText = text
queryTextChangedJob?.cancel()
queryTextChangedJob = lifecycleScope.launch(Dispatchers.Main) {
println("async work started...")
delay(2000)
doSearch()
println("async work done!")
}
return false
}
如果您想在 ViewModel 中使用launch
,请使用implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.0-alpha05"
queryTextChangedJob = viewModelScope.launch(Dispatchers.Main) {
//...
}
使用协程和流程:
private fun SearchView.onQueryTextChanged(): ReceiveChannel<String> =
Channel<String>(capacity = Channel.UNLIMITED).also { channel ->
setOnQueryTextListener(object : SearchView.OnQueryTextListener{
override fun onQueryTextSubmit(query: String?): Boolean {
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
newText.orEmpty().let(channel::offer)
return true
}
})
}
@ExperimentalCoroutinesApi
fun <T> ReceiveChannel<T>.debounce(time: Long, unit: TimeUnit = TimeUnit.MILLISECONDS, scope: CoroutineScope): ReceiveChannel<T> =
Channel<T>(capacity = Channel.CONFLATED).also { channel ->
scope.launch {
var value = receive()
whileSelect {
onTimeout(time) {
channel.offer(value)
value = receive()
true
}
onReceive {
value = it
true
}
}
}
}
并像这样添加到您的搜索视图中:
lifecycleScope.launch {
searchView.onQueryTextChanged().debounce(time = 500, scope = lifecycleScope).consumeEach { newText ->
//Use debounced query
}
}
我没有找到使我能够使用诸如distinctUntilChanged
之类的流运算符的解决方案,我尝试了这种方法,也许有人需要它:
首先从SearchView创建一个扩展函数
private fun SearchView.getQueryTextChangeStateFlow(): StateFlow<String> {
val query = MutableStateFlow("")
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
return true
}
override fun onQueryTextChange(newText: String): Boolean {
query.value = newText
return true
}
})
return query
}
然后
private fun setUpSearchStateFlow(searchView: SearchView) {
lifecycleScope.launch {
searchView.getQueryTextChangeStateFlow()
.debounce(300)
.distinctUntilChanged()
.map { query ->
startSearchRequestHere(query)
}
.flowOn(Dispatchers.Default)
.collect {}
}
}
我最终得到了类似于下面的解决方案。 这样它应该每半秒触发一次。
public boolean onQueryTextChange(final String newText) {
if (newText.length() > 3) {
if (canRun) {
canRun = false;
handler.postDelayed(new Runnable() {
@Override
public void run() {
canRun = true;
handleLocationSearch(newText);
}
}, 500);
}
}
return false;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.