[英]Android DiffUtil inconsistency exceptions when using custom added views like i.e. for empty lists
I need help with the DiffUtil for RecyclerView Adapter. 我需要有关DiffUtil for RecyclerView Adapter的帮助。 I have made a Custom Adapter with the possibility to add custom Views like a loading view or empty view etc. Everything works fine without using DiffUtil but when I use it I have sometimes an inconsistency detected exception. 我制作了一个自定义适配器,可以添加自定义视图,例如加载视图或空视图等。在不使用DiffUtil的情况下一切正常,但是当我使用它时,有时会检测到不一致情况。 I think the problem lies within the getItemCount() method but I'm not sure. 我认为问题出在getItemCount()方法之内,但我不确定。 If someone could give me an advise it would be very helpful. 如果有人可以给我建议,那将非常有帮助。 Here is the code I'm using. 这是我正在使用的代码。
This is my DiffUtil Adapter Class: 这是我的DiffUtil适配器类:
public abstract class DiffRecyclerViewAdapter<T extends DiffComparable<T>> extends BaseRecyclerViewAdapter<T> {
private DifferenceCalculator<T> mDiffCalculator = new DifferenceCalculator<>(
new DifferenceCalculator.DiffCallback<>(getPayloadCallback()),
new UpdateCallback(this),
TaskExecutor.getInstance().getExecutor());
public DiffRecyclerViewAdapter(Context mContext, List<T> itemList) {
super(mContext, itemList);
setDataIsLoading(true);
}
public DiffRecyclerViewAdapter(Context mContext, List<T> itemList, int itemsLeftToLoadMore, LoadMoreDataCallback listener) {
super(mContext, itemList, itemsLeftToLoadMore, listener);
setDataIsLoading(true);
}
public void setItemList(List<T> list) {
if (isLoading()) {
setDataIsLoading(false, true);
}
mDiffCalculator.calculateDifference(list);
}
@Override
public int getItemCount() {
int superCount = super.getItemCount();
log("getItemCount() called with count=" + mDiffCalculator.getCurrentList().size());
return superCount > mDiffCalculator.getCurrentList().size() ? superCount : mDiffCalculator.getCurrentList().size();
}
@Override
public List<T> getList() {
return mDiffCalculator.getCurrentList();
}
protected abstract PayloadCallback<T> getPayloadCallback();
protected void onNewList() {
}
protected void onItemsInserted(int position, int count) {
log("onItemsInserted(position=" + position + ", count=" + count + ")");
notifyItemRangeInserted(position, count);
}
@SuppressWarnings("WeakerAccess")
protected void onItemsRemoved(int position, int count) {
log("onItemsRemoved(position=" + position + ", count=" + count + ")");
notifyItemRangeRemoved(position, count);
}
@SuppressWarnings("WeakerAccess")
protected void onItemMoved(int fromPosition, int toPosition) {
log("onItemMoved(fromPosition=" + fromPosition + ", toPosition=" + toPosition + ")");
notifyItemMoved(fromPosition, toPosition);
}
@SuppressWarnings("WeakerAccess")
protected void onItemsChanged(int position, int count, @Nullable Object payload) {
log("onItemsChanged(position=" + position + ", count=" + count + ", payload=" + payload + ")");
notifyItemRangeChanged(position, count, payload);
}
public void log(String msg) {
if (IN_DEBUG_MODE) {
Log.i(getClass().getSimpleName(), msg);
}
}
public static class UpdateCallback implements ListUpdateCallback {
private DiffRecyclerViewAdapter adapter;
private UpdateCallback(DiffRecyclerViewAdapter adapter) {
this.adapter = adapter;
}
@SuppressWarnings("WeakerAccess")
public void onUpdateStart() {
Log.w(getClass().getSimpleName(), "onUpdateStart()");
adapter.setListUpdateInProgress(true);
}
@SuppressWarnings("WeakerAccess")
public void onUpdateFinish() {
Log.w(getClass().getSimpleName(), "onUpdateFinish()");
adapter.setListUpdateInProgress(false);
}
@SuppressWarnings("WeakerAccess")
public void onNewList() {
adapter.onNewList();
}
@Override
public void onInserted(int position, int count) {
adapter.onItemsInserted(position, count);
}
@Override
public void onRemoved(int position, int count) {
adapter.onItemsRemoved(position, count);
}
@Override
public void onMoved(int fromPosition, int toPosition) {
adapter.onItemMoved(fromPosition, toPosition);
}
@Override
public void onChanged(int position, int count, @Nullable Object payload) {
adapter.onItemsChanged(position, count, payload);
}
}
public interface PayloadCallback<T> {
@Nullable
Object getChangePayload(T oldItem, T newItem);
}
}
And this is my Difference Calculator Class: 这是我的差异计算器类:
public class DifferenceCalculator<T extends DiffComparable<T>> {
private MainThreadExecutor mMainThreadExecutor;
private Executor mBackgroundThreadExecutor;
private DiffCallback<T> mDiffUtilCallback;
private DiffRecyclerViewAdapter.UpdateCallback mUpdateCallback;
private List<T> mList;
private List<T> mReadOnlyList = Collections.emptyList();
private int mMaxScheduledGeneration;
@SuppressWarnings("unused")
public DifferenceCalculator(DiffCallback<T> diffCallback, DiffRecyclerViewAdapter.UpdateCallback callback) {
this(diffCallback, callback, null);
}
@SuppressWarnings("WeakerAccess")
public DifferenceCalculator(DiffCallback<T> diffCallback,
DiffRecyclerViewAdapter.UpdateCallback callback,
Executor backgroundThreadExecutor) {
mDiffUtilCallback = diffCallback;
mUpdateCallback = callback;
mMainThreadExecutor = new MainThreadExecutor();
mBackgroundThreadExecutor = backgroundThreadExecutor != null ? backgroundThreadExecutor :
new ThreadPoolExecutor(
2,
2,
30,
TimeUnit.SECONDS,
new LinkedBlockingQueue<>()
);
}
@SuppressWarnings("WeakerAccess")
@NonNull
public List<T> getCurrentList() {
return mReadOnlyList;
}
@SuppressWarnings("WeakerAccess")
public void calculateDifference(@Nullable final List<T> newList) {
final int runGeneration = ++mMaxScheduledGeneration;
log("calculating difference for process=" + runGeneration);
mUpdateCallback.onUpdateStart();
if (newList == mList) {
mUpdateCallback.onUpdateFinish();
log("abandoned because no change!");
return;
}
if (newList == null) {
int countRemoved = mList.size();
mList = null;
mReadOnlyList = Collections.emptyList();
mUpdateCallback.onRemoved(0, countRemoved);
mUpdateCallback.onUpdateFinish();
log("New List is null!");
return;
}
if (mList == null) {
mList = newList;
mReadOnlyList = Collections.unmodifiableList(newList);
mUpdateCallback.onInserted(0, newList.size());
mUpdateCallback.onNewList();
mUpdateCallback.onUpdateFinish();
log("Complete new List arrived.");
return;
}
final List<T> oldList = mList;
mBackgroundThreadExecutor.execute(() -> {
final DiffUtil.DiffResult result = DiffUtil.calculateDiff(new DiffUtil.Callback() {
@Override
public int getOldListSize() {
return oldList.size();
}
@Override
public int getNewListSize() {
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
T oldItem = oldList.get(oldItemPosition);
T newItem = newList.get(newItemPosition);
if (oldItem != null && newItem != null) {
return mDiffUtilCallback.areItemsTheSame(oldItem, newItem);
}
return oldItem == null && newItem == null;
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
T oldItem = oldList.get(oldItemPosition);
T newItem = newList.get(newItemPosition);
if (oldItem != null && newItem != null) {
return mDiffUtilCallback.areContentsTheSame(oldItem, newItem);
}
if (oldItem == null && newItem == null) {
return true;
}
throw new AssertionError();
}
@Nullable
@Override
public Object getChangePayload(int oldItemPosition, int newItemPosition) {
T oldItem = oldList.get(oldItemPosition);
T newItem = newList.get(newItemPosition);
if (oldItem != null && newItem != null) {
return mDiffUtilCallback.getChangePayload(oldItem, newItem);
}
throw new AssertionError();
}
});
mMainThreadExecutor.execute(() -> {
if (mMaxScheduledGeneration == runGeneration) {
dispatchResult(newList, result);
} else {
mUpdateCallback.onUpdateFinish();
log("result not dispatched because other pending calculations in queue!");
}
});
});
}
private void dispatchResult(@NonNull List<T> newList, @NonNull DiffUtil.DiffResult diffResult) {
log("dispatching result");
mList = newList;
mReadOnlyList = Collections.unmodifiableList(newList);
diffResult.dispatchUpdatesTo(mUpdateCallback);
mUpdateCallback.onUpdateFinish();
}
private static class MainThreadExecutor implements Executor {
private Handler handler = new Handler(Looper.getMainLooper());
@Override
public void execute(Runnable command) {
handler.post(command);
}
}
public static class DiffCallback<T extends DiffComparable<T>> {
private DiffRecyclerViewAdapter.PayloadCallback<T> callback;
public DiffCallback(DiffRecyclerViewAdapter.PayloadCallback<T> callback){
this.callback = callback;
}
boolean areItemsTheSame(T oldItem, T newItem) {
return oldItem.isItemTheSame(newItem);
}
boolean areContentsTheSame(T oldItem, T newItem) {
return oldItem.isContentTheSame(newItem);
}
@Nullable
public Object getChangePayload(T oldItem, T newItem) {
if(callback != null){
return callback.getChangePayload(oldItem, newItem);
}
return null;
}
}
private void log(String msg){
if(IN_DEBUG_MODE){
Log.w(getClass().getSimpleName(), msg);
}
}
}
From what I can read from the stacktrace it seems that the adapter or RecyclerView receives a wrong position for the last item. 从我从stacktrace中读取的内容来看,适配器或RecyclerView似乎对最后一个项目接收了错误的位置。 It is always trying to get a Viewholder for the last item with position == itemCount and of course this is not working because the positions start at 0. So why doesn't it receive position == (itemCount - 1) for the last item? 它总是尝试为位置== itemCount的最后一个项目获取Viewholder,当然,这是不起作用的,因为位置从0开始。所以为什么它不为最后一个项目接收位置==(itemCount-1) ?
Solved it! 解决了! The problem occurred when my empty view was visible and I inserted a new not empty list. 当我的空视图可见并且我插入了一个新的非空列表时,发生了问题。 I forgot to call notifyItemChanged(0) before I inserted the new list. 在插入新列表之前,我忘记了调用notifyItemChanged(0)。 I changed the onUpdateStart() method of my Update callback class and everything is working fine. 我更改了Update回调类的onUpdateStart()方法,一切正常。 Here is the change 这是改变
@SuppressWarnings("WeakerAccess")
public void onUpdateStart() {
Log.w(getClass().getSimpleName(), "onUpdateStart()");
adapter.setListUpdateInProgress(true);
if(adapter.isEmptyViewVisible()){
adapter.notifyItemChanged(0);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.