2015-09-02 3 views
7

У меня возникла проблема с использованием метода notifyItemMoved(). Кажется, это неверно отображает невозмущенные представления.RecyclerView развращает просмотр с помощью notifyItemMoved()

В моем списке 4 элемента. То, что я хочу сделать, - это оживить обмен между пунктом 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<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 имел изменения, и это вообще отображается. Кто-нибудь знает, почему?

EDIT

я могу обойти выше, обернув через весь адаптер и вызов notifyItemChanged() по каждому пункту. Неэффективное и нехорошее решение, но оно невидимо для пользователя.

+0

Итак, я думаю, вы говорите, что 'onBindViewHolder (owner, 1)' не следует вызывать, правильно? Я также не знаю, почему он * называется *. Но я также думаю, что вам нужно просто называть 'notifyItemChanged (i); notifyItemChanged (indexOfCorrectItem); 'вместо' notifyItemMoved (i, indexOfCorrectItem); 'Вы пробовали это? –

+0

Нет, проблема в том, что элемент 2 меняет свое отображение. onBindViewHolder следует вызывать для 1 и 3, потому что notifyItemMoved (1, 3) отмечает, что оба они нуждаются в перерисовании. То, что не должно происходить, должно быть затронуто во 2-м пункте, его не следует трогать. – Graeme

+0

Я не знаю, почему RecyclerView ведет себя точно так, как он есть, но я * могу понять, что он смущен тем, что вы делаете: вы начали с '[0, 1, 2, 3]'. Вызывая 'notifyItemChanged (1, 3)' вы говорите: «Я удалил позицию позиции 1, вставленную в позицию 3». Чтобы это было верно, ваш список теперь должен быть '[0, 2, 3, 1]'. Но на самом деле это '[0, 3, 2, 1]', поэтому, если он затем вызывает 'onBindViewHolder (владелец, 1)' *, конечно *, он увидит '3' и перезапишет' 2', что был там раньше. Меня озадачивает только то, что он чувствует необходимость даже называть это ... –

ответ

12

Благодарим вас за @ 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); 

Это левый список в правильном порядке, но это короткое замыкание анимации.

Таким образом, вместо этого, я сбрасывал обменивать вообще:

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

Спасибо за подробное объяснение: вы просмотрели код RecyclerView достаточно, чтобы понять, почему notifyItemChanged (1) был вызван вообще (ваш шаг 4)? Если вы сообщите об этом адаптеру, то «Элемент 1 будет удален, поэтому поместите элемент 2 вниз на один слот», он не должен полностью перепроверять этот элемент. По крайней мере, не более того, что впоследствии понадобилось бы переподтвердить следующий элемент ... –

+0

Я обнаружил, что используя 'Коллекции.swap (dataSet, fromPosition, toPosition); notifyItemMoved (fromPosition, toPosition); notifyItemChanged (fromPosition); notifyItemChanged (toPosition); ' сохранил как анимации, так и обновил любые неверные привязки держателя списка, которые использовали старые значения позиции – saganaut

+0

У меня была аналогичная проблема при попытке обновления значений элементов. Мой recyclerview показывал дубликаты/corrompted пункты, в то время как у адаптера была правильная информация, поэтому проблема была в recyclerview, а не в адаптере. Я должен был сделать то же самое, что и @Graeme. Спасибо. item.setCantidad (item.getCantidad() + newItem.getCantidad()); mitems.remove (позиция); mitems.add (item); notifyItemMoved (позиция, mitems.size() - 1); notifyItemChanged (mitems.size() - 1); – Herman

1

Я была такая же проблема. RecyclerView-Items повреждены при перетаскивании & drop. Но я нашел простое решение: В вашем RecyclerView.Adapter.class обязательно иметь следующие

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

Вы должны вернуть правильный Itemid для позиции. С этого момента Элементы не повреждены.

0

Чтобы получить фактическое положение вашего пункта после того, как перетащить & капли, добавьте этот метод адаптер:

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.

0

Ну, я обработал его несколько иначе, может помочь другим.

 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. Он не убивает анимацию и работает как ожидалось.