Android: как использовать обозреватель для нескольких критериев фильтра? - PullRequest
2 голосов
/ 01 октября 2019

У меня есть список ReViewlerView CardViews. Я хотел бы, чтобы пользователь мог фильтровать список на основе типов CardView, нажимая на разные TextViews для типов. Фильтр работает правильно с первого раза. Если я нажимаю на One TextView, список фильтруется, и в представлении отображаются только карты One. Возврат возвращает пользователя к полному списку. Далее пользователь нажимает на Two TextView. Здесь возникает проблема, так как в представлении отображаются отфильтрованные карты списка «один», а не предполагаемые две карты. Таким образом, наблюдатель по умолчанию использует исходный тип фильтра «Одна карта», а не ожидаемый тип фильтра «Две карты».

Та же ошибка возникает, если пользователь сначала выбирает TextView с двумя картами. В этом случае только две карты отображаются в представлении, как и ожидалось. Пробел возвращает пользователя в полный список. Затем щелчок по One TextView снова фильтрует список, но показывает две карты, а не ожидаемые карты One.

Что мне здесь не хватает? Я пытался использовать removeObserver () и removeObservers () после каждого нажатия на TextView, но безуспешно. Как мне повторно использовать наблюдателя с несколькими критериями фильтра, чтобы мне не приходилось настраивать отдельных наблюдателей для каждого фильтра, который затем потребовал бы отдельных методов в 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;
       }
   }
}

Ответы [ 2 ]

1 голос
/ 05 октября 2019

Прочитайте эту удивительную статью: MediatorLiveData для спасения

Вы должны наблюдать несколько источников с вашей точки зрения. Цитирую статью: «Путь к этому называется MediatorLiveData: подкласс LiveData, который может наблюдать другие объекты LiveData и реагировать на них событиями OnChanged». Я уверен, что у вас все будет хорошо, но, если у вас возникли проблемы с созданием Java-кода, дайте мне знать.

Кроме того, не мешало бы проверить и это: MediatorLiveData

1 голос
/ 03 октября 2019

В вашем коде есть несколько проблем.

  • вызывайте cardsAdapter.notifyDataSetChanged() каждый раз после установки новых данных для адаптера.

  • thismethod

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;
}

Перезагрузите его каждый раз - не проверяйте на ноль.

Надеюсь, это поможет.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...