Реализация флажка в Recylerview. Флажок MVVM Room ViewModel LiveData Recylerview

Я реализовал Java-приложение для Android с шаблоном MVVM с Room, ViewModel, LiveData, Recylerview.

  1. В recyclerview есть список элементов, и в каждом элементе есть флажок.
  2. Когда флажок в элементе recyclerview включен или отключен, модель обновляется в базе данных комнаты через ViewModel.
  3. Чтобы прослушивать событие включения или отключения флажка для каждого элемента recyclerview, я реализовал OnCheckedChangeListener.

Это нормально с recyclerview с 1 ​​или 2 предметами,

Но когда количество элементов recyclerview увеличивается, например> 10, при включении или отключении флажка другие элементы в recyclerview также страдают.

Например:

Если я изменяю флажок в элементе [5] (идентификатор элемента равен 21, флажок включен), он также запускает OnCheckedChangeListener для других элементов, таких как элемент [1] (идентификатор элемента равен 15, флажок включен) и элемент [8] ( ID товара 18, чекбокс включен).

Logcat:

2019-04-25 11:15:33.343 9890-9890/com.hardian.mvvm.sample D/RVAdapter: setOnCheckedChange--- ID :21 isRetryEnabled; false isChecked true
2019-04-25 11:15:33.405 9890-9890/com.hardian.mvvm.sample D/RVAdapter: setOnCheckedChange--- ID :15 isRetryEnabled; false isChecked true
2019-04-25 11:15:33.415 9890-9890/com.hardian.mvvm.sample D/RVAdapter: setOnCheckedChange--- ID :18 isRetryEnabled; false isChecked true

Код: в моем StatusRecyclerViewAdapter

 @Override
    public void onBindViewHolder(StatusViewHolder holder, int position) {
        StatusItem  statusItem = mDataSet.get(position);

        if (statusItem != null) {
            cbStatus.setChecked(statusItem.isRetryEnabled());

            cbStatus.setOnCheckedChangeListener((buttonView, isChecked) -> {
                Log.d("RVAdapter", "setOnCheckedChange--- ID :" + statusItem.getId()+ " isRetryEnabled; "+statusItem.isRetryEnabled()+" isChecked "+isChecked);
                //update the model via the activitiy's viewmodel through the onStatusCheckBoxChangeListener interface.
                if (onStatusCheckBoxChangeListener != null)
                    onStatusCheckBoxChangeListener.onStatusCheckBoxChanged(statusItem,isChecked);
            });

        }
    }

Мне нужно решение, чтобы исправить эту проблему с переработкой Recylerview.

Примечание. Я пробовал this.setIsRecyclable(false);, проблема устранилась, но это не рекомендуется, мне нужно найти другой механизм, чтобы избежать этого.


person Hardian    schedule 25.04.2019    source источник


Ответы (4)


  1. Установите тег на свой флажок и получите элемент из тега.
  2. Не используйте setOnCheckedChangeListener, вместо этого используйте setOnClickListener.

Код:

cbStatus.setTag(statusItem);
cbStatus.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        CheckBox cb = (CheckBox) v;
        StatusItem item = (StatusItem) cb.getTag();
        if (onStatusCheckBoxChangeListener != null)
            onStatusCheckBoxChangeListener.onStatusCheckBoxChanged(item,cb.isChecked());
    }
});
person Whatman    schedule 25.04.2019
comment
Спасибо за это, отлично сработало. Пометка сработала, теперь проблема с несколькими триггерами исчезла. - person Hardian; 25.04.2019
comment
Вау, спасибо за часть setTag. Не знал, что с его помощью можно установить класс модели для просмотра. - person Vygintas B; 25.04.2019

Я думаю, вам нужно получить позицию через holder.getAdapterPosition(). Тогда он должен быть правильным.

Почему? Потому что:

Обратите внимание, что в отличие от ListView, RecyclerView не будет вызывать этот метод снова, если позиция элемента изменяется в наборе данных, если только сам элемент не признан недействительным или новая позиция не может быть определена. ... Если вам понадобится позиция элемента позже (например, в прослушивателе кликов), используйте getAdapterPosition (), который будет иметь обновленную позицию адаптера.

Также: установка onClickListener внутри onBindViewHolder - не лучшая практика, поскольку каждый раз, когда вызывается onBindViewHolder, устанавливается слушатель. Установите его в onCreateViewHolder, поскольку onCreate вызывается только тогда, когда необходимо создать новый ViewHolder, и, следовательно, гораздо реже.

person Rene Ferrari    schedule 25.04.2019
comment
Разве Viewholder тоже не перерабатывается? Я имею в виду, что все методы вызываются для каждого элемента. - person Vygintas B; 25.04.2019
comment
Не у каждого элемента есть собственный ViewHolder. ViewHolders может получить другой элемент при прокрутке, так как гораздо дешевле просто установить новый элемент, а не раздуть полный макет. - person Rene Ferrari; 25.04.2019
comment
Правда. Кроме того, вам не нужно использовать экземпляр держателя при доступе к элементам. - person Vygintas B; 25.04.2019

Хитрость заключается в том, чтобы установить флажок в неактивном состоянии и выполнить ручную проверку самостоятельно. Чтобы это работало, вы должны сохранить его статус в списке данных и обновить его в соответствии со значением из CheckBox.

Первый набор CheckBox не активен.

    android:clickable="false"
    android:focusable="false"
    android:focusableInTouchMode="false"


Затем настройте свой ClickListener

  holder.cbStatus.setOnClickListener(v -> {
        StatusItem  statusItem = mDataSet.get(position);

        if (cbStatus.isChecked()) {
            cbStatus.setChecked(false);
            statusItem.setChecked(false);

        } else {
            cbStatus.setChecked(true);
            statusItem.setChecked(true);
        }
 });
person Vygintas B    schedule 25.04.2019
comment
Я понимаю, что прослушиватель щелчка работает вместо прослушивателя проверенных изменений. Спасибо за эту информацию. Используя здесь шаблон Viewmodel Livedata Room, обновление набора данных осуществляется только через объект AndroidViewModel. - person Hardian; 25.04.2019

вы должны установить оператор else, потому что представление является переработанным и использует «старое» представление.

или вы можете сделать что-то вроде этого:

    @Override
public void onBindViewHolder(StatusViewHolder holder, int position) {
    StatusItem  statusItem = mDataSet.get(position);

    youCheckBox.setChecked(false);

    if (statusItem != null) {
        cbStatus.setChecked(statusItem.isRetryEnabled());

        cbStatus.setOnCheckedChangeListener((buttonView, isChecked) -> {
            Log.d("RVAdapter", "setOnCheckedChange--- ID :" + statusItem.getId()+ " isRetryEnabled; "+statusItem.isRetryEnabled()+" isChecked "+isChecked);
            //update the model via the activitiy's viewmodel through the onStatusCheckBoxChangeListener interface.
            if (onStatusCheckBoxChangeListener != null)
                onStatusCheckBoxChangeListener.onStatusCheckBoxChanged(statusItem,isChecked);
        });

    }
}
person Ronny Bigler    schedule 25.04.2019