我通过onCreateOptionsMenu(菜单菜单)在工具栏上有一个SearchView图标。单击该图标将创建一个EditText行,以供用户输入搜索输入。我在EditText行上附加了一个TextWatcher(),以便在将文本添加到该行(afterTextChanged Editable)时,通过ViewModel运行searchQuery方法来搜索Room数据库。
我的问题是,即使在用户输入任何查询之前,观察者也会在创建时触发。使用Toasts,我能够在用户按下SearchView图标时以及在用户输入任何搜索查询之前,确认观察者正在运行空白查询“ %%”。即使在afterTextChanged()中设置了观察者,也会发生这种情况。如何让ViewModel观察器仅在用户输入文本后才触发?
Activity
...
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.mainactiv_menu, menu);
searchItem = menu.findItem(R.id.action_search);
menu.findItem(R.id.action_search).setVisible(false);
if (cardsAdapter != null && cardsAdapter.getItemCount() > 0) {
menu.findItem(R.id.action_search).setVisible(true);
}
SearchManager searchManager = (SearchManager) MainActivity.this.getSystemService(Context.SEARCH_SERVICE);
if (searchItem != null) {
mSearchView = (SearchView) searchItem.getActionView();
if (mSearchView != null) {
mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
EditText mSearchEditText = mSearchView.findViewById(androidx.appcompat.R.id.search_src_text);
mSearchEditText.setInputType(android.text.InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
mSearchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// not needed
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// not needed
}
@Override
public void afterTextChanged(Editable s) {
String queryText = "%" + s.toString() + "%";
mQuickcardViewModel.searchQuery(queryText).observe(MainActivity.this, searchCards -> {
searchList = searchCards;
if (!mSearchView.isIconified() && searchList.size() > 0) {
// do something
}
...
答案 0 :(得分:3)
如何让ViewModel观察器仅在用户输入文本后才触发?
在观察之前使用if
语句查看字符串是否为空:
@Override
public void afterTextChanged(Editable s) {
if (s.toString().length() > 0) {
// the rest of your code goes here, preferably with some modifications
}
}
我建议的其他更改包括:
添加一些“去抖动”的度量。给定您的搜索表达式,您似乎正在访问searchQuery()
中的一个数据库。如果用户快速连续键入10个字符,则将进行10个查询,这将使性能变差。 “反跳”表示“仅在用户暂停一会后才进行查询”,例如500毫秒,而无需任何其他输入。
添加智能以在需要时取消未完成的查询。假设用户键入了一点,然后经过了反跳周期。因此,您将触发查询。由于您的LIKE
(假定)表达式,您的查询将执行“表扫描”,检查表的每一行。这可能很慢,并且用户可能会在第一个查询完成之前开始输入更多内容。您不再需要该查询,因为它的结果是错误的(它用于上一个搜索表达式,而不是当前的搜索表达式)。因此,您需要一种取消这项工作的方法。
处理配置更改。您可能会争辩说您正在使用ViewModel
和LiveData
,它们应该能够处理...并且确实可以,但前提是您正确使用它们。在您的情况下,您似乎在观察LiveData
后就将其丢弃,依此类推,在进行配置更改时,您将无法重新观察它。这就是为什么许多样本专注于将“请做背景工作”与“请给我背景工作的结果”分离的原因。您仍然可以使用一个searchQuery()
方法,但是它将返回void
。结果将通过LiveData
持有的一些ViewModel
传递,您可以在onCreate()
中观察到。这样,在配置更改后,您将再次观察到LiveData
,并获得该配置更改之前的数据。
使用FTS。 LIKE
很慢。如果您打算这样做,请考虑在SQLite中使用FTS3 / FTS4进行全文搜索。如果您使用的是Room,则Room 2.2.0中对此提供内置支持。