Мне нужна помощь с адаптером DiffUtil для RecyclerView. Я сделал пользовательский адаптер с возможностью добавления пользовательских представлений, таких как представление загрузки или пустое представление и т. Д. Все отлично работает без использования DiffUtil, но при его использовании иногда возникает исключение, обнаруженное несогласованность. Я думаю, что проблема заключается в методе getItemCount (), но я не уверен. Если бы кто-то мог дать мне совет, это было бы очень полезно. Вот код, который я использую.
Это мой класс адаптера 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);
}
}
А это мой класс калькулятора различий:
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);
}
}
}
Из того, что я могу прочитать из трассировки стека, кажется, что адаптер или RecyclerView получает неправильную позицию для последнего элемента. Он всегда пытается получить видоискатель для последнего элемента с position == itemCount, и, конечно, это не работает, потому что позиции начинаются с 0. Так почему он не получает позицию == (itemCount - 1) для последнего элемента