我在DiffUtil.ItemCallback
库中遇到问题,其中列表项的旧状态以某种方式最终丢失并导致oldTask
具有与newTask
.
当我选中/取消选中 ListAdapter 中的复选框并onChanged()
在 LiveData 观察者中调用DiffUtil.ItemCallback
函数时,就会发生这种情况。正如您在里面看到onBindViewHolder()
的那样,该动作调用setCompleted()
否定isCompleted()
. 过期任务(红色)的预期行为是在选中复选框时变为白色。这是因为isOverdue()
return 时只能返回isCompleted()
false 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,显示了应用程序的行为方式。如您所见,即使选中,列表项也是红色的,即使未选中,列表项也是白色的。您还可以看到一些列表项正确呈现,但不是全部。
差异回调:
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()
因为当您退出并重新进入活动时,它会正确呈现。