首页 > 解决方案 > Android:如何将观察者用于多个过滤条件?

问题描述

我有一个 CardViews 的 RecyclerView 列表。我希望用户能够通过单击类型的不同 TextView 来根据 CardView 类型过滤列表。过滤器第一次正常工作。如果我单击 One TextView,则会过滤列表,并且视图仅显示 One 卡片。退格键将用户带回到完整列表。接下来,用户单击两个 TextView。这就是问题发生的地方,因为视图显示的是过滤后的一张卡片列表,而不是预期的两张卡片。因此观察者默认使用原始的一张卡过滤器类型,而不是预期的两张卡过滤器类型。

如果用户先选择两张卡的TextView,也会出现同样的错误。在这种情况下,只有两张卡片按预期显示在视图中。退格将用户返回到完整列表。然后单击 One TextView 再次过滤列表,但显示两张卡片而不是预期的一张卡片。

我在这里想念什么?每次单击 TextView 后,我都尝试使用 removeObserver() 和 removeObservers() 但没有运气。如何重新使用具有多个过滤条件的观察者,这样我就不必为每个过滤器设置单独的观察者,然后需要在 ViewModel、Repository 和 Dao 中使用单独的方法?

public class MainActivity extends AppCompatActivity {

    // for filtering card type
    final String type0 = "OneCards";
    final String type1 = "TwoCards";

    private List<Card> filteredModelList0 = null;
    private List<Card> filteredModelList1 = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mViewModel = new ViewModelProvider(this).get(ViewModel.class);
    }

    public void onClickFilterList(View view) {

        // set up an AlertDialog for the user to select a filter
        final AlertDialog.Builder alertDialogFilter = new AlertDialog.Builder(MainActivity.this);
        LayoutInflater inflaterFilter = getLayoutInflater();
        final ViewGroup nullParent = null;
        final View dialogLayoutFilter = inflaterFilter.inflate(R.layout.filter_main_aldialog, nullParent);
        alertDialogFilter.setView(dialogLayoutFilter);
        final AlertDialog dialogFilter = alertDialogFilter.create();
        dialogFilter.show();

        TextView allOnes = dialogLayoutFilter.findViewById(R.id.AllOnes);
        TextView allTwos = dialogLayoutFilter.findViewById(R.id.AllTwos);

        allOnes.setOnClickListener(v -> {

            mViewModel.getFilteredList(type0).observe(this, filterList -> {

                filteredModelList0 = filterList;
                if (filterList.size() == 0) {
                    Toast.makeText(MainActivity.this, "There are no 'One' cards", Toast.LENGTH_SHORT).show();
                    filteredModelList0 = null;
                } else {
                    cardsAdapter.setCardList(filteredModelList0);
                    Toast.makeText(MainActivity.this, "There are 'One' cards", Toast.LENGTH_SHORT).show();
                }
            });
            dialogFilter.dismiss();
        });

        allTwos.setOnClickListener(v -> {

            mViewModel.getFilteredList(type1).observe(this, filterList -> {

                filteredModelList1 = filterList;
                if (filterList.size() == 0) {
                    Toast.makeText(MainActivity.this, "There are no 'Two' cards", Toast.LENGTH_SHORT).show();
                    filteredModelList1 = null;
                } else {
                    cardsAdapter.setCardList(filteredModelList1);
                    Toast.makeText(MainActivity.this, "There are 'Two' cards", Toast.LENGTH_SHORT).show();
                }
           });
          dialogFilter.dismiss();
       });
    }

ViewModel

public class ViewModel extends AndroidViewModel {

    …
    private MutableLiveData<List<Card>> filteredList = null;

    public LiveData<List<Card>> getFilteredList(String cardType) {
        if (filteredList == null) {
            filteredList = new MutableLiveData<>();
            loadFilteredCards(cardType);
        }
        return filteredList;
    }

    public void loadFilteredCards(String cardType){
        filteredList.postValue(repository.getFilteredCards(cardType));
    }

Adapter
…
public class Adapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {

   public List<Card> mListItems;
   …
   public void setCardList(List<Card> newList) {

       if (mListItems != null) {
           PostDiffCallback postDiffCallback = new PostDiffCallback(mListItems, newList);
           DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(postDiffCallback);

           mListItems.clear();
           mListItems.addAll(newList);
           diffResult.dispatchUpdatesTo(this);
       } else { // first initialization.
           mListItems = newList;
       }
   }
}

标签: androidandroid-recyclerviewfilteringviewmodel

解决方案


您的代码中有几个问题。

  • cardsAdapter.notifyDataSetChanged()每次将新数据设置为适配器后调用。

  • 这种方法

public LiveData<List<Card>> getFilteredList(String cardType) {
        if (filteredList == null) {
            filteredList = new MutableLiveData<>();
            loadFilteredCards(cardType);
        }
        return filteredList;
}

查看此代码。如果filteredList为空,则用数据填充此列表 - 这是您第一次检索数据时发生的事情。第二次尝试过滤 - 你只这样做return filteredList;- 因此你返回你已经在之前的过滤器中形成的相同列表。- 你使用的LiveData很不正确 - 它在你的代码中作为实际数据的一些奇怪的无用代理工作。

我建议这样做

// For listeners
...
allOnes.setOnClickListener(v -> {
                filteredModelList0 = mViewModel.getFilteredList(type0);
                ...
                cardsAdapter.setCardList(filteredModelList0);
                cardsAdapter.notifyDataSetChanged()
                ...
        });
allTwos.setOnClickListener(v -> {
                filteredModelList1 = mViewModel.getFilteredList(type1);
                ...
                cardsAdapter.setCardList(filteredModelList1);
                cardsAdapter.notifyDataSetChanged()
                ...
        });
...

//For ViewModel
public class ViewModel extends AndroidViewModel {

    public LiveData<List<Card>> getFilteredList(String cardType) {
        return repository.getFilteredCards(cardType);
    }

}

这可能需要一些额外的工作,但我希望你能做到。

编辑

如果你真的想使用这样LiveData的编辑getFilteredList()

public LiveData<List<Card>> getFilteredList(String cardType) {
        filteredList = new MutableLiveData<>();
        loadFilteredCards(cardType);
        return filteredList;
}

每次都重新加载它——不要检查空值。

希望能帮助到你。


推荐阅读