notifyitemmoved在recyclerview中执行意外交换

stszievb  于 2021-06-30  发布在  Java
关注(0)|答案(0)|浏览(382)

我使用itemtouchhelper类来支持在我的recyclerview中拖放。当我拖动一个项目时,它会像预期的那样可视化地更新(交换行)。一旦我删除了该项,就会发生意外的“最后一次交换”,即在被拖动的项的原始位置的新值被插入到recyclerview的拖动位置,并且从拖动的项开始的所有项都被上移一行。这听起来很混乱,所以我有一个图表来演示这种行为。如果我将“a”从位置0拖动到位置3,recyclerview会在位置3显示“b”(现在是位置0),所有以“a”开头的项目都会向上移动。简言之,拖动操作“直观地”执行了两次。在这种情况下,从0到3,然后再从0到3。


这很奇怪,因为我记录了currentlist变量的内容,它显示了正确的顺序。一旦我删除了一个项目,我也会更新它在房间数据库中的位置。由于列表是通过livedata观察的,它将更新后的列表返回给mainactivity并调用onchanged,后者使用正确排序的列表(我也通过记录其内容进行了验证)将submitlist调用给适配器。
如果我关闭并重新打开应用程序,将显示正确排序的列表。这只是某种“视觉交换”,我不知道是什么原因造成的。任何帮助都将不胜感激!
编辑:如果我在这个奇怪的交换发生后调用notifydatasetchanged(),列表会执行一些不寻常的动画,但是排序正确(所以我的逻辑是正确的)。但是我使用listadapter作为我的recycleradapter和diffutil来更新我的列表。notifydatasetchanged效率低下,并且破坏了使用diffutil的整个目的。
主活动.java

private void setListObserver() {
    viewModel.getAllItems().observe(this, new Observer<List<ListItem>>() {
      @Override
      // I have verified newList has the correct order through log statements
      public void onChanged(List<ListItem> newList) { 
        adapterMain.submitList(newList);
      }
    });
  }
...

// This method is called when item starts dragging
public void onSelectedChanged(@Nullable RecyclerView.ViewHolder viewHolder, int actionState) {
  ...

  if (actionState == ItemTouchHelper.ACTION_STATE_DRAG) { 
    currentList = new ArrayList<>(adapterMain.getCurrentList()); // get current list from adapter
  }
  ...
}

// This method is called when item is dropped
public void clearView(@NonNull RecyclerView recyclerView,
                            @NonNull RecyclerView.ViewHolder viewHolder) {
...

  // I have verified that all code in this method is returning correct values through log statements.
  // If I restart the app, everything is in the correct order

  // this is position of the where the item was dragged to, gets its value from the onMoved method. 
  // it's the last "toPos" value in onMoved() after the item is dropped
  int position = toPosition; 

    // Skip this code if item was deleted (indicated by -1). Otherwise, update the moved item
    if(position != -1) {
      ListItem movedItem = currentList.get(position);

      // If dragged to the beginning of the list, subtract 1 from the previously lowest
      // positionInList value (the item below it) and assign it the moved item. This will ensure
      // that it now has the lowest positionInList value and will be ordered first.
      if(position == 0) {
        itemAfterPos = currentList.get(position + 1).getPositionInList();
        movedItemNewPos = itemAfterPos - 1;

        // If dragged to the end of list, add 1 to the positionInList value of the previously
        // largest value and assign to the moved item so it will be ordered last.
      } else if (position == (currentList.size() - 1)) {

        itemBeforePos = currentList.get(position - 1).getPositionInList();
        movedItemNewPos = itemBeforePos + 1;

        // If dragged somewhere in the middle of list, get the positionInList variable value of
        // the items before and after it. They are used to compute the moved item's new
        // positionInList value.
      } else {

        itemBeforePos = currentList.get(position - 1).getPositionInList(); 
        itemAfterPos = currentList.get(position + 1).getPositionInList();

        // Calculates the moved item's positionInList variable to be half way between the
        // item above it and item below it
        movedItemNewPos = itemBeforePos + ((itemAfterPos - itemBeforePos) / 2.0);
      }
      updateItemPosInDb(movedItem, movedItemNewPos);
    }

  private void updateItemPosInDb(ListItem movedItem, double movedItemNewPos) {
    movedItem.setPositionInList(movedItemNewPos);
    viewModel.update(movedItem); // this updates the database which triggers the onChanged method 
  }

  public void onMoved(@NonNull RecyclerView recyclerView,
                          @NonNull RecyclerView.ViewHolder source, int fromPos,
                          @NonNull RecyclerView.ViewHolder target, int toPos, int x, int y) {
    Collections.swap(currentList, toPos, fromPos);
    toPosition = toPos; // used in clearView()
    adapterMain.notifyItemMoved(fromPos, toPos); // Not sure if this method is somehow causing the issue
  }
}).attachToRecyclerView(recyclerMain);

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题