拖拽后调用ListAdapter的submitList更新RecyclerView更高效的方式

gig*_*ig6 6 java android android-recyclerview itemtouchhelper android-listadapter

我正在使用 ItemTouchHelper 类来支持 RecyclerView 中的拖放。我有一个按预期工作的实现,但它似乎效率很低。在我的 onMoved 方法中,我获取列表的当前版本,交换拖动的项目,然后使用新版本的列表调用 SubmitList。问题是,每次发生交换时都会调用 onMoved,并且交换在列表中拖动时会一一执行。因此,如果位置 1 处的项目被拖动到位置 11,则每次交换时都会调用 onMoved (1 -> 2) 、 (2 -> 3) 等。这意味着我获得了列表的新版本,并在拖动完成时调用了 SubmitList 10 次。

我尝试过的:

我尝试在拖动完成后使用更新的列表调用submitList,但它没有改变。我在调用submitList之前记录了适配器的getCurrentList(),它显示了原始的未更改列表(这是预期的)。我已经记录了 MainActivity 的 currentList 变量,它显示了更新的交换列表。我将 currentList 变量传递给submitList,然后记录适配器的 getCurrentList(),它仍然显示原始未更改的列表。我已阅读这篇文章,但建议的解决方案均无效。任何帮助,将不胜感激!

编辑:我还在适配器中使用 DiffUtil。

MainActivity.java

...

private void createItemTouchHelper() {
    new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
        ItemTouchHelper.UP | ItemTouchHelper.DOWN,
        ItemTouchHelper.START | ItemTouchHelper.END) {

    // To track changes when user drags
    List<ListItem> currentList;

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

        // get the current list from the adapter to modify
        // have to create new list because getCurrentList() returns an unmodifiable list
        currentList = new ArrayList<>(adapterMain.getCurrentList()); 
      }
    }

    // This method gets called when user releases the dragged item
    public void clearView(@NonNull RecyclerView recyclerView,
                            @NonNull RecyclerView.ViewHolder viewHolder) { 

      adapterMain.submitList(currentList); // Does not update the list in the adapter
    }

    // gets called each time a row is dragged by one position
    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); // update the local ArrayList

        // This "visually" moves the item in RecyclerView but it doesn't update the list in the adapter
        adapterMain.notifyItemMoved(fromPos, toPos); 
    }
  }).attachToRecyclerView(recyclerMain);
}
Run Code Online (Sandbox Code Playgroud)

这是 onMoved 方法的工作版本。我不必在 onSelectedChanged() 或clearView() 中添加任何代码即可使其工作。虽然看起来效率很低...

public void onMoved(@NonNull RecyclerView recyclerView,
                          @NonNull RecyclerView.ViewHolder source, int fromPos,
                          @NonNull RecyclerView.ViewHolder target, int toPos, int x, int y) {


        // adapterMain.getCurrentList() returns an unmodifiable list, convert to new ArrayList
        currentList = new ArrayList<>(adapterMain.getCurrentList());
        Collections.swap(currentList, toPos, fromPos);
        adapterMain.submitList(currentList); // why does submitList work here but not in the clearView method?
        // adapterMain.notifyItemMoved(fromPos, toPos); // this is not needed when I call submitList here
      }
Run Code Online (Sandbox Code Playgroud)

RecyclerAdapterMain.java

public class RecyclerAdapterMain extends ListAdapter<ListItem, RecyclerAdapterMain.ListItemHolder> {

  public RecyclerAdapterMain() {
    super(DIFF_CALLBACK);
  }

    private static final DiffUtil.ItemCallback<ListItem> DIFF_CALLBACK = new DiffUtil.ItemCallback<ListItem>() {
    @Override
    public boolean areItemsTheSame(@NonNull ListItem oldItem, @NonNull ListItem newItem) {
      return oldItem.getId() == newItem.getId();
    }

    @Override
    public boolean areContentsTheSame(@NonNull ListItem oldItem, @NonNull ListItem newItem) {
      return oldItem.equals(newItem);
    }
  };

...

}
Run Code Online (Sandbox Code Playgroud)