在SearchView中调节onQueryTextChange

时间:2016-01-22 20:10:47

标签: java android search searchview android-search

"节流"最好的方法是什么? onQueryTextChange这样我的performSearch()方法每秒只调用一次而不是每次用户输入一次?

public boolean onQueryTextChange(final String newText) {
    if (newText.length() > 3) {
        // throttle to call performSearch once every second
        performSearch(nextText);
    }
    return false;
}

8 个答案:

答案 0 :(得分:18)

基于aherrick的代码,我有一个更好的解决方案。而不是使用布尔值“canRun”,声明一个runnable变量,并在每次更改查询文本时清除处理程序上的回调队列。这是我最终使用的代码:

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

答案 1 :(得分:5)

我使用RxJava找到了解决方案,尤其是debounce运营商。

凭借杰克沃顿的方便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`
            }
        });

答案 2 :(得分:2)

您可以使用RxJava轻松实现它。此外,您将需要RxAndroidRxBinding(但如果您使用的是RxJava,则可能已在项目中使用它们。)

RxTextView.textChangeEvents(yourEditText)
          .debounce(1, TimeUnit.SECONDS)
          .observeOn(AndroidSchedulers.mainThread())
          .subscribe(performSearch());

Here Kaushik Gopal的完整例子。

答案 3 :(得分:2)

如果您使用的是Kotlin和协同程序,则可以执行以下操作:

var queryTextChangedJob: Job? = null

...

fun onQueryTextChange(query: String): Boolean {

    queryTextChangedJob?.cancel()

    queryTextChangedJob = launch(Dispatchers.Main) {
        delay(500)
        performSearch(query)
    }
}

答案 4 :(得分:1)

对于科林

对于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) {
        //...
}

答案 5 :(得分:0)

我最终得到了类似于下面的解决方案。这样它应该每半秒发射一次。

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

答案 6 :(得分:0)

1)创建抽象类:

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 onDelayerQueryTextChange(String query);
}

2)像这样设置:

searchView.setOnQueryTextListener(new DelayedOnQueryTextListener() {
        @Override
        public void onDelayerQueryTextChange(String query) {
            // Handle query
        }
    });

答案 7 :(得分:0)

使用协程和流程:

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