如何让 ViewModel 观察者最初不观察?
How to get ViewModel observer to not observe initially?
我通过 onCreateOptionsMenu(菜单菜单)在工具栏上有一个 SearchView 图标。单击该图标会创建一个 EditText 行,供用户输入搜索内容。我有一个 TextWatcher() 附加到 EditText 行,这样当文本被添加到该行时(afterTextChanged Editable s),searchQuery 方法是 运行 通过搜索 Room 数据库的 ViewModel。
我的问题是观察者甚至在用户输入任何查询之前就在创建时触发。使用 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
}
...
How do I get the ViewModel observer to only fire after text is entered by the user?
在观察前使用if
语句判断字符串是否为空:
@Override
public void afterTextChanged(Editable s) {
if (s.toString().length() > 0) {
// the rest of your code goes here, preferably with some modifications
}
}
我建议的其他更改包括:
添加一些 "debounce" 的度量。根据您的搜索表达式,您似乎正在访问 searchQuery()
中的数据库。如果用户快速连续键入 10 个字符,您将进行 10 次查询,这会使性能变差。 "Debounce" 表示 "only query after the user has paused for a bit",例如 500ms 没有任何进一步的输入。
添加智能以在需要时取消未完成的查询。假设用户键入了一点,然后去抖周期结束。所以,你触发了一个查询。由于您的(假定的)LIKE
表达式,您的查询将执行 "table scan",检查 table 的每一行。这可能会很慢,并且用户可能会在第一个查询完成之前开始输入更多内容。您不再需要该查询,因为它的结果是错误的(它是针对先前的搜索表达式,而不是当前的)。因此,您需要一种方法来取消该工作。
正在处理配置更改。您可能会争辩说您使用的是 ViewModel
和 LiveData
,它们应该可以处理该问题……确实如此,但前提是您正确使用它们。在您的情况下,您似乎在观察 LiveData
之后丢弃了它,等等配置更改,您将无法重新观察它。这就是为什么很多样本都专注于将 "please do the background work" 与 "please give me the results of the background work" 解耦。您仍然可以调用 searchQuery()
方法,但它会 return void
。结果将通过 ViewModel
持有的一些 LiveData
管道传输,您在 onCreate()
中观察到的那个。这样,在配置更改后,您会再次观察到 LiveData
,并获得配置更改之前的数据。
使用 FTS。 LIKE
很慢。如果您要经常这样做,请考虑在 SQLite 中使用 FTS3/FTS4 进行全文搜索。如果您使用的是 Room,Room 2.2.0 中对此有内置支持。
我通过 onCreateOptionsMenu(菜单菜单)在工具栏上有一个 SearchView 图标。单击该图标会创建一个 EditText 行,供用户输入搜索内容。我有一个 TextWatcher() 附加到 EditText 行,这样当文本被添加到该行时(afterTextChanged Editable s),searchQuery 方法是 运行 通过搜索 Room 数据库的 ViewModel。
我的问题是观察者甚至在用户输入任何查询之前就在创建时触发。使用 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
}
...
How do I get the ViewModel observer to only fire after text is entered by the user?
在观察前使用if
语句判断字符串是否为空:
@Override
public void afterTextChanged(Editable s) {
if (s.toString().length() > 0) {
// the rest of your code goes here, preferably with some modifications
}
}
我建议的其他更改包括:
添加一些 "debounce" 的度量。根据您的搜索表达式,您似乎正在访问
searchQuery()
中的数据库。如果用户快速连续键入 10 个字符,您将进行 10 次查询,这会使性能变差。 "Debounce" 表示 "only query after the user has paused for a bit",例如 500ms 没有任何进一步的输入。添加智能以在需要时取消未完成的查询。假设用户键入了一点,然后去抖周期结束。所以,你触发了一个查询。由于您的(假定的)
LIKE
表达式,您的查询将执行 "table scan",检查 table 的每一行。这可能会很慢,并且用户可能会在第一个查询完成之前开始输入更多内容。您不再需要该查询,因为它的结果是错误的(它是针对先前的搜索表达式,而不是当前的)。因此,您需要一种方法来取消该工作。正在处理配置更改。您可能会争辩说您使用的是
ViewModel
和LiveData
,它们应该可以处理该问题……确实如此,但前提是您正确使用它们。在您的情况下,您似乎在观察LiveData
之后丢弃了它,等等配置更改,您将无法重新观察它。这就是为什么很多样本都专注于将 "please do the background work" 与 "please give me the results of the background work" 解耦。您仍然可以调用searchQuery()
方法,但它会 returnvoid
。结果将通过ViewModel
持有的一些LiveData
管道传输,您在onCreate()
中观察到的那个。这样,在配置更改后,您会再次观察到LiveData
,并获得配置更改之前的数据。使用 FTS。
LIKE
很慢。如果您要经常这样做,请考虑在 SQLite 中使用 FTS3/FTS4 进行全文搜索。如果您使用的是 Room,Room 2.2.0 中对此有内置支持。