Исключения несоответствия Android DiffUtil при использовании пользовательских добавленных представлений, таких как, например, для пустых списков - PullRequest
0 голосов
/ 09 марта 2019

Мне нужна помощь с адаптером 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) для последнего элемента

1 Ответ

0 голосов
/ 11 марта 2019

Решено!Проблема возникла, когда мой пустой вид был виден, и я вставил новый непустой список.Я забыл вызвать notifyItemChanged (0), прежде чем вставить новый список.Я изменил метод onUpdateStart () моего класса обратного вызова Update, и все работает нормально.Вот изменение

 @SuppressWarnings("WeakerAccess")
 public void onUpdateStart() {
    Log.w(getClass().getSimpleName(), "onUpdateStart()");
    adapter.setListUpdateInProgress(true);
    if(adapter.isEmptyViewVisible()){
         adapter.notifyItemChanged(0);
    }
  }
...