如何使ViewModel观察者最初不观察?

时间:2019-10-22 22:55:41

标签: android android-viewmodel android-textwatcher

我通过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
                    } 

                    ...     

1 个答案:

答案 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(假定)表达式,您的查询将执行“表扫描”,检查表的每一行。这可能很慢,并且用户可能会在第一个查询完成之前开始输入更多内容。您不再需要该查询,因为它的结果是错误的(它用于上一个搜索表达式,而不是当前的搜索表达式)。因此,您需要一种取消这项工作的方法。

  • 处理配置更改。您可能会争辩说您正在使用ViewModelLiveData,它们应该能够处理...并且确实可以,但前提是您正确使用它们。在您的情况下,您似乎在观察LiveData后就将其丢弃,依此类推,在进行配置更改时,您将无法重新观察它。这就是为什么许多样本专注于将“请做背景工作”与“请给我背景工作的结果”分离的原因。您仍然可以使用一个searchQuery()方法,但是它将返回void。结果将通过LiveData持有的一些ViewModel传递,您可以在onCreate()中观察到。这样,在配置更改后,您将再次观察到LiveData,并获得该配置更改之前的数据。

  • 使用FTS。 LIKE很慢。如果您打算这样做,请考虑在SQLite中使用FTS3 / FTS4进行全文搜索。如果您使用的是Room,则Room 2.2.0中对此提供内置支持。