RecyclerView使用notifyItemMoved()损坏视图

我遇到了使用notifyItemMoved()方法的问题。 这似乎是错误地显示unmoved的意见。

我的列表中有4个元素。 我想要做的是在项目1和项目3之间进行交换animation。项目1和3交换正确,但项目2显示项目3的内容!

所以名单开始看起来像这样:

 Item 0 Item 1 Item 2 Item 3 

并且像这样结束:

 Item 0 Item 3 Item 3 <!-- What the heck has this changed for? Item 1 

我的适配器由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(); } } } 

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

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

我的日志如下所示:

 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) 

正如你所看到的,项目2根本没有理由改变它的显示 – 但是,它确实有。 有人知道为什么?

编辑

我可以通过遍历整个适配器并在每个项目上调用notifyItemChanged()来解决上述问题。 效率低下并不是一个好的解决scheme,但是对用户是无形的。

Solutions Collecting From Web of "RecyclerView使用notifyItemMoved()损坏视图"

感谢@ david.mihola让我知道我做错了什么。

由于症状没有使问题变得明显,所以花了这么长时间才弄清楚了!

我正在这样做:

 Collections.swap(mProductItems, i, indexOfCorrectItem); notifyItemMoved(i, indexOfCorrectItem) 

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

在封面下面是这样做的:

  1. 将项目1移动到3
  2. 移动2到1来填补空白
  3. 移动3到2来填补空白
  4. notifyItemChanged(1);
  5. notifyItemChanged(3);

当然,上面的项目3没有刷新的视图下移到项目2! 这是第4步和第5步隐藏问题,使item1和item3正确显示,并使item2不正确!

只要我意识到这一点,我尝试了下面的代码:

 notifyItemMoved(indexOfCorrectItem, i); notifyItemMoved(i, indexOfCorrectItem); 

这个按照正确的顺序离开了这个列表,但是它把animation短路了。

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

 mProductItems.remove(indexOfCorrectItem); mProductItems.add(i, correctItem); notifyItemMoved(indexOfCorrectItem, i); 

我遇到过同样的问题。 RecyclerView-Items在拖放时损坏。 但是我find了一个简单的解决scheme:在你的RecyclerView.Adapter.class中一定要有以下内容

 @Override public long getItemId(int position) { // here code for getting the right itemID, // ie return super.getItemId(mPosition); // where mPosition ist the Position in the Collection. } 

您必须为位置返回正确的itemID。 从现在开始,物品并没有腐败。

要在拖放之后获取物品的实际位置,请将此方法添加到适配器中:

 private int getItemPosition(Item item){ // (<-- replace with your item) int i = 0; // (replace with your items and methods here) for (Item currentItem : mItems) { if (currentItem.getItemId() == item.getItemId()) break; i++; } return i; } 

并调用这个而不是viewHolder给出的位置。

那么,我以一种稍微不同的方式处理,可能会帮助别人。

  Collections.swap(mItemList, fromPosition, toPosition); // Need to do below, because NotifyItemMove only handle one sided move Item fromItem = mItemList.get(fromPosition); Item toItem = mItemList.get(toPosition); notifyItemChanged(fromPosition, toItem); notifyItemChanged(toPosition, fromItem); 

我不得不重新排列网格上的项目,并将位置保存到一个文件。 @Graeme是对的,但我不想放弃交换。 所以就像@saganaut我坚持notifyItemChanged。 但是,只有使用notifyItemChanged有时会在同一个项目上留下网格上交换的项目,所以我使用notifyItemChanged绑定项目。 这不是杀死animation,而是按预期工作。