DiffUtil.ItemCallback иногда теряет oldItem и заставляет areContentsTheSame () неправильно возвращать true - PullRequest
0 голосов
/ 03 февраля 2020

У меня проблема с библиотекой DiffUtil.ItemCallback, когда старое состояние элемента списка каким-то образом теряется и приводит к тому, что oldTask принимает те же значения, что и newTask.

Это происходит, когда я устанавливаю / снимаю флажок в моем ListAdapter и onChanged() в моем наблюдателе LiveData вызывает функции DiffUtil.ItemCallback. Как вы можете видеть внутри onBindViewHolder(), это действие вызывает setCompleted(), что сводит на нет значение isCompleted(). Ожидаемое поведение для просроченной задачи (красного цвета) - окрашивание в белый цвет всякий раз, когда установлен флажок. Это связано с тем, что isOverdue() может возвращать false только тогда, когда isCompleted() возвращает true.

onBindViewHolder ():

@Override
public void onBindViewHolder(@NonNull ViewHolder viewHolder, int i) {
    SubTask subTask = getItem(i);
    Log.d(TAG, "onBindViewHolder: CALLED");
    viewHolder.subTaskName.setText(subTask.getName());

    Calendar dueDate = subTask.getDueDate();
    viewHolder.dueDate.setText(context.getResources().getString(R.string.due_date, dueDate.getDisplayName(Calendar.DAY_OF_WEEK, Calendar.LONG, Locale.getDefault()), dueDate.getDisplayName(Calendar.MONTH, Calendar.LONG, Locale.getDefault()), dueDate.get(Calendar.DAY_OF_MONTH), dueDate.get(Calendar.YEAR)));

    viewHolder.checkBox.setOnCheckedChangeListener(null);
    //check of sub task if completed
    if (subTask.isCompleted()) {
        viewHolder.checkBox.setChecked(true);
    } else {
        viewHolder.checkBox.setChecked(false);
    }

    if (subTask.isOverdue()) {
        viewHolder.card.setCardBackgroundColor(context.getColor(R.color.red));
        viewHolder.subTaskName.setTextColor(context.getColor(R.color.colorAccent));
        viewHolder.dueDate.setTextColor(context.getColor(R.color.colorAccent));
    } else {
        viewHolder.card.setCardBackgroundColor(context.getColor(R.color.colorAccent));
        viewHolder.subTaskName.setTextAppearance(R.style.TextAppearance_AppCompat_Large);
        viewHolder.dueDate.setTextColor(context.getColor(R.color.main_task_text_color));
    }

    viewHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            Log.d(TAG, "onCheckedChanged: called for subTask " + subTask.getName() + " isChecked = " + isChecked);
            viewModel = ViewModelProviders.of((FragmentActivity) context).get(ViewSubTasksForMainTaskViewModel.class);
            if (isChecked) {
                //if overdue, change card to white
                subTask.setCompleted(true);
                viewModel.updateSubTask(subTask);
            } else {
                //if overdue, set color back to red
                subTask.setCompleted(false);
                viewModel.updateSubTask(subTask);
            }
        }
    });

}

Однако, когда areContentsTheSame() пытается сравнить значения в старом и новом списках, иногда возвращает true, когда этого не следует делать, так как isCompleted() должен возвращать разные значения для старого и нового элементов списка. Кажется, что старое состояние элемента списка каким-то образом потеряно. Это приводит к тому, что элементы списка отображаются неправильно, поскольку onBindViewHolder() не вызывается. Вот GIF , который показывает, как приложение ведет себя. Как видите, элементы списка имеют красный цвет, даже если они отмечены, а элементы списка - белые, даже если они не отмечены. Вы также можете видеть, что некоторые элементы списка отображаются правильно, но не все из них.

DiffCallback:

public class SubTaskDiffCallback {

private static final String TAG = "SubTaskDiffCallback";

static public DiffUtil.ItemCallback<SubTask> getSubTaskDiffCallback() {
    return new DiffUtil.ItemCallback<SubTask>() {
        @Override
        public boolean areItemsTheSame(@NonNull SubTask oldTask, @NonNull SubTask newTask) {
            Log.d(TAG, "areItemsTheSame: " + oldTask.getName() + " " + newTask.getName() + " " + Boolean.toString(oldTask.getId() == newTask.getId()));
            return oldTask.getId() == newTask.getId();
        }

        @Override
        public boolean areContentsTheSame(@NonNull SubTask oldTask, @NonNull SubTask newTask) {
            Log.d(TAG, "areContentsTheSame: oldTask " + oldTask.toString());
            Log.d(TAG, "areContentsTheSame: newTask " + newTask.toString());
            boolean contentsSame = oldTask.getName().equals(newTask.getName()) &&
                    oldTask.getDueDate().equals(newTask.getDueDate()) &&
                    oldTask.isCompleted() == newTask.isCompleted() &&
                    oldTask.getMainTaskId() == (newTask.getMainTaskId());
            Log.d(TAG, "areContentsTheSame = " + contentsSame);
            return contentsSame;
        }

        @Nullable
        @Override
        public Object getChangePayload(@NonNull SubTask oldTask, @NonNull SubTask newTask) {
            if (oldTask.isCompleted() != newTask.isCompleted()) {
                Log.d(TAG, "getChangePayload = false");
                return Boolean.FALSE;
            } else {
                return null;
            }
        }
    };
}
}

Вот неполный журнал событий, связанных в GIF-файле. выше:

2020-02-02 15:45:21.536 8226-8226/com.johnsorhannus.divideandconquer D/ViewSTForMTAdapter: onCheckedChanged: called for subTask Wash car isChecked = true
2020-02-02 15:45:21.555 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Wash car Wash car true
2020-02-02 15:45:21.555 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read OS book Read OS book true
2020-02-02 15:45:21.556 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read database book Read database book true
2020-02-02 15:45:21.556 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read database book Read database book true
2020-02-02 15:45:21.556 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read OS book Read OS book true
2020-02-02 15:45:21.556 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Wash car Wash car true
2020-02-02 15:45:21.557 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Wash car, dueDate = Mar 17, 2019, overdue = false, completed = true]
2020-02-02 15:45:21.557 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Wash car, dueDate = Mar 17, 2019, overdue = false, completed = true]
2020-02-02 15:45:21.557 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:21.557 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Read OS book, dueDate = Mar 20, 2019, overdue = true, completed = false]
2020-02-02 15:45:21.558 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Read OS book, dueDate = Mar 20, 2019, overdue = true, completed = false]
2020-02-02 15:45:21.558 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:21.558 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Read database book, dueDate = Mar 23, 2019, overdue = true, completed = false]
2020-02-02 15:45:21.558 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Read database book, dueDate = Mar 23, 2019, overdue = true, completed = false]
2020-02-02 15:45:21.558 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:22.979 8226-8226/com.johnsorhannus.divideandconquer D/ViewSTForMTAdapter: onCheckedChanged: called for subTask Read OS book isChecked = true
2020-02-02 15:45:22.999 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Wash car Wash car true
2020-02-02 15:45:22.999 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read OS book Read OS book true
2020-02-02 15:45:22.999 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read database book Read database book true
2020-02-02 15:45:22.999 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read database book Read database book true
2020-02-02 15:45:22.999 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read OS book Read OS book true
2020-02-02 15:45:22.999 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Wash car Wash car true
2020-02-02 15:45:23.000 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Wash car, dueDate = Mar 17, 2019, overdue = false, completed = true]
2020-02-02 15:45:23.001 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Wash car, dueDate = Mar 17, 2019, overdue = false, completed = true]
2020-02-02 15:45:23.001 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:23.002 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Read OS book, dueDate = Mar 20, 2019, overdue = true, completed = false]
2020-02-02 15:45:23.002 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Read OS book, dueDate = Mar 20, 2019, overdue = false, completed = true]
2020-02-02 15:45:23.002 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = false
2020-02-02 15:45:23.003 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Read database book, dueDate = Mar 23, 2019, overdue = true, completed = false]
2020-02-02 15:45:23.004 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Read database book, dueDate = Mar 23, 2019, overdue = true, completed = false]
2020-02-02 15:45:23.004 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:23.004 8226-8226/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: getChangePayload = false
2020-02-02 15:45:23.014 8226-8226/com.johnsorhannus.divideandconquer D/ViewSTForMTAdapter: onBindViewHolder: CALLED
2020-02-02 15:45:24.275 8226-8226/com.johnsorhannus.divideandconquer D/ViewSTForMTAdapter: onCheckedChanged: called for subTask Read database book isChecked = true
2020-02-02 15:45:24.294 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Wash car Wash car true
2020-02-02 15:45:24.294 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read OS book Read OS book true
2020-02-02 15:45:24.294 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read database book Read database book true
2020-02-02 15:45:24.294 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read database book Read database book true
2020-02-02 15:45:24.294 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read OS book Read OS book true
2020-02-02 15:45:24.294 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Wash car Wash car true
2020-02-02 15:45:24.295 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Wash car, dueDate = Mar 17, 2019, overdue = false, completed = true]
2020-02-02 15:45:24.295 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Wash car, dueDate = Mar 17, 2019, overdue = false, completed = true]
2020-02-02 15:45:24.295 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:24.296 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Read OS book, dueDate = Mar 20, 2019, overdue = false, completed = true]
2020-02-02 15:45:24.296 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Read OS book, dueDate = Mar 20, 2019, overdue = false, completed = true]
2020-02-02 15:45:24.296 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:24.296 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Read database book, dueDate = Mar 23, 2019, overdue = true, completed = false]
2020-02-02 15:45:24.297 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Read database book, dueDate = Mar 23, 2019, overdue = false, completed = true]
2020-02-02 15:45:24.297 8226-11254/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = false
2020-02-02 15:45:24.297 8226-8226/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: getChangePayload = false
2020-02-02 15:45:24.308 8226-8226/com.johnsorhannus.divideandconquer D/ViewSTForMTAdapter: onBindViewHolder: CALLED
2020-02-02 15:45:26.590 8226-8226/com.johnsorhannus.divideandconquer D/ViewSTForMTAdapter: onCheckedChanged: called for subTask Read database book isChecked = false
2020-02-02 15:45:26.608 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Wash car Wash car true
2020-02-02 15:45:26.608 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read OS book Read OS book true
2020-02-02 15:45:26.608 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read database book Read database book true
2020-02-02 15:45:26.608 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read database book Read database book true
2020-02-02 15:45:26.608 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Read OS book Read OS book true
2020-02-02 15:45:26.608 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areItemsTheSame: Wash car Wash car true
2020-02-02 15:45:26.609 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Wash car, dueDate = Mar 17, 2019, overdue = false, completed = true]
2020-02-02 15:45:26.609 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Wash car, dueDate = Mar 17, 2019, overdue = false, completed = true]
2020-02-02 15:45:26.609 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:26.610 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Read OS book, dueDate = Mar 20, 2019, overdue = false, completed = true]
2020-02-02 15:45:26.610 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Read OS book, dueDate = Mar 20, 2019, overdue = false, completed = true]
2020-02-02 15:45:26.610 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true
2020-02-02 15:45:26.611 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: oldTask SubTask[name = Read database book, dueDate = Mar 23, 2019, overdue = true, completed = false]
2020-02-02 15:45:26.611 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame: newTask SubTask[name = Read database book, dueDate = Mar 23, 2019, overdue = true, completed = false]
2020-02-02 15:45:26.611 8226-11255/com.johnsorhannus.divideandconquer D/SubTaskDiffCallback: areContentsTheSame = true

Я точно знаю, что setCompleted() изменил значение isCompleted(), потому что при выходе и повторном входе в действие, правильно отображает .

1 Ответ

1 голос
/ 03 февраля 2020

DiffUtil сохраняет старые элементы по ссылке - они не копируются. Ваш класс SubTask является изменяемым, поэтому, когда вы вызываете setCompleted, вы меняете старый, на который также ссылается DiffUtil. DiffUtil может сравниваться только со списком старых, неизмененных объектов. Поскольку вы изменили старый объект, и у него есть только ссылка на объект, который вы сейчас изменили, он не знает правильного старого значения.

...