RecyclerView 通知xxx

RecyclerView notifyxxx

最近从 notifyDataSetChanged 切换到 notifyItemInserted/Changed/Removed 以保留动画。

我陷入了这种情况。我的适配器列表最初是:

A
B
C
D

然后我合并 A、B 和 C,在最后位置插入一个新的 E 项目:

A 
E

我想我可以通知索引 1 (B) 已删除,索引 2 (C) 已删除,索引 3 (D) 已删除,然后新索引 1 (E) 已插入,但异常来自我不知道哪里说:

Inconsistency detected. Invalid view holder adapter positionViewHolder...

所以我认为可能是索引 1 通知两次引起的问题,所以我将其更改为通知索引 1 (B) 已更改为 E,索引 2 (C) 已删除,索引 3 (D)已删除,但抛出相同的异常,让我别无选择。

在这种情况下,正确的做法应该是什么?

不要让问题没有答案,以防其他人遇到同样的问题

随着支持库 24.2 Diffutils 的发布,它有助于 class 计算两组项目之间的差异。

示例代码可能看起来像这样(创建两个列表并按下按钮交换适配器中的项目)。 DiffUtil.DiffResult 将处理所有 notify 个电话。

    final List<RecyclerObject> list = new ArrayList<>();
    list.add(new RecyclerObject("A"));
    list.add(new RecyclerObject("B"));
    list.add(new RecyclerObject("C"));
    list.add(new RecyclerObject("D"));

    final TestAdapter adapter = new TestAdapter(list);
    recycler.setAdapter(adapter);

    final List<RecyclerObject> newList = new ArrayList<>();
    newList.add(new RecyclerObject("A"));
    newList.add(new RecyclerObject("E"));

    final View button = findViewById(R.id.mainButton);
    button.setOnClickListener(v -> {
        adapter.setList(newList);

        DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallback(list, newList));
        diffResult.dispatchUpdatesTo(adapter);
    });

Diffcallback 是自己创建的 class,实现可能看起来像这样。 (为了更好的可读性删除了不必要的代码)

public class DiffCallback extends DiffUtil.Callback {

    DiffCallback(final List<RecyclerObject> list, final List<RecyclerObject> newList) {
        this.list = list;
        this.newList = newList;
    }

    @Override
    public int getOldListSize() {
        return list.size();
    }

    @Override
    public int getNewListSize() {
        return newList.size();
    }

    @Override
    public boolean areContentsTheSame(final int oldItemPosition, final int newItemPosition) {
        return list.get(oldItemPosition).title.equals(newList.get(newItemPosition).title);
    }

}

还有一种不太花哨的方法:如果我们可以保证支持适配器的项目是 "stable" 并且相同的项目将始终具有相同的标识符,recycler.adapter 允许 setHasStableIds选项。这样一来,即使仅使用 notifyDatasetChanged(通常会杀死动画)也能正确处理它们。需要覆盖函数 getItemId()