RecyclerView使用notifyItemMoved()破坏视图

Gra*_*eme 8 android recycler-adapter android-recyclerview

我在使用该notifyItemMoved()方法时遇到问题.它似乎错误地显示无动于衷的视图.

我的清单中有4个元素.我想要做的是动画项目1和项目3之间的交换.项目1和3交换正确,但项目2显示项目3的内容!

所以列表开始看起来像这样:

Item 0
Item 1
Item 2
Item 3
Run Code Online (Sandbox Code Playgroud)

并以此结束:

Item 0
Item 3
Item 3 <!-- What the heck has this changed for?
Item 1
Run Code Online (Sandbox Code Playgroud)

我的适配器由a支持List mProductList.我调用以下代码:

public void sortBackingListUsingSortingList(List<ProductWrapper> newProductItems) {
    Log.e("", "Before:");
    for(ProductWrapper wrapper : mProductItems) wrapper.log();
    for(int i = 0; i < newProductItems.size(); i++) {
        ProductWrapper currentItem   = mProductItems.get(i);
        ProductWrapper correctItem   = newProductItems.get(i);

        if(!currentItem.equals(correctItem)) {
            // Item in wrong place
            int indexOfCorrectItem = getIndexOfItemInList(mProductItems, correctItem);
            Collections.swap(mProductItems, i, indexOfCorrectItem);
            notifyItemMoved(i, indexOfCorrectItem);
            Log.e("", "notifyItemMoved(" + i + ", " + indexOfCorrectItem+")");
            Log.e("", "After:");
            for(ProductWrapper wrapper : mProductItems) wrapper.log();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我还添加了日志记录以onBindViewHolder检查我的视图逻辑是否被调用:

@Override
public void onBindViewHolder(HolderBasic holder, int position) {
    Log.e("", "onBindViewHolder(holder, " + position + ")");
    holder.fill(mProductItems.get(position));
}
Run Code Online (Sandbox Code Playgroud)

我的日志看起来像这样:

09-02 14:39:17.853  ? Before:
09-02 14:39:17.853  : Item 0
09-02 14:39:17.853  : Item 1
09-02 14:39:17.853  : Item 2
09-02 14:39:17.853  : Item 3

09-02 14:39:17.854  ? notifyItemMoved(1, 3)

09-02 14:39:17.854  ? After:
09-02 14:39:17.854  : Item 0
09-02 14:39:17.854  : Item 3
09-02 14:39:17.854  : Item 2
09-02 14:39:17.854  : Item 1

09-02 14:39:17.867  ? onBindViewHolder(holder, 1)
09-02 14:39:17.874  ? onBindViewHolder(holder, 3)
Run Code Online (Sandbox Code Playgroud)

正如你所看到的,第2项没有理由改变它的显示 - 然而,确实如此.谁知道为什么?

编辑

我可以通过遍历整个适配器并调用notifyItemChanged()每个项目来绕过上面.效率低而且不是一个好的解决方案,但对用户来说是不可见的.

Gra*_*eme 16

感谢@ david.mihola带领我做错了什么.

这花了很长时间才弄清楚,因为症状没有使问题显而易见!

我这样做:

Collections.swap(mProductItems, i, indexOfCorrectItem);
notifyItemMoved(i, indexOfCorrectItem)
Run Code Online (Sandbox Code Playgroud)

但是,我显然没有想到notifyItemMoved()实际上在做什么.它只是通知适配器项i已移动到indexOfCorrectItem 它不会告诉适配器也indexOfCorrectItem已移动到i.

在幕后,它正在做以下事情:

  1. 将项目1移至3
  2. 移动2比1,以填补空白
  3. 移动3到2的位置以填补空白
  4. notifyItemChanged(1);
  5. notifyItemChanged(3);

当然,上面的第3项将第3项移至项目2而没有更新的视图!步骤4和5通过使item1和item3正确显示并使item2不正确来隐藏问题!

一旦我意识到这一点,我尝试了以下代码:

notifyItemMoved(indexOfCorrectItem, i);
notifyItemMoved(i, indexOfCorrectItem);
Run Code Online (Sandbox Code Playgroud)

这使列表以正确的顺序排列,但它使动画短路.

所以,相反,我完全抛弃了交换:

mProductItems.remove(indexOfCorrectItem);
mProductItems.add(i, correctItem);
notifyItemMoved(indexOfCorrectItem, i);
Run Code Online (Sandbox Code Playgroud)

  • 我发现使用`Collections.swap(dataSet, fromPosition, toPosition); notifyItemMoved(fromPosition, toPosition); notifyItemChanged(fromPosition); notifyItemChanged(toPosition);` 保留了两个动画,并更新了使用旧位置值的任何不正确的视图持有者绑定 (3认同)