RecyclerView使用notifyItemMoved()破坏视图

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

我的清单中有4个元素。 我想要做的是animation项目1和项目3之间的交换。项目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 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()来解决上述问题。 效率低而且不是一个好的解决方案,但对用户来说是不可见的。

感谢@ 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了一个简单的解决方案:在您的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,并且按预期工作。