我通过onCreateOptionsMenu(菜单菜单)在工具栏上有一个SearchView图标。单击该图标将创建一个EditText行,以供用户输入搜索输入。我有一个TextWatcher()附加到EditText行,以便将文本添加到该行(afterTextChanged Editable)时,将通过搜索Room数据库的ViewModel运行searchQuery方法。
我的问题是,即使在用户输入任何查询之前,观察者也会在创建时触发。使用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
}
...
如何使ViewModel观察器仅在用户输入文本后才触发?
在观察之前使用if
语句查看字符串是否为空:
@Override
public void afterTextChanged(Editable s) {
if (s.toString().size > 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中对此提供了内置支持。