为什么在 RecyclerView 中重复拖动动画?

如何解决为什么在 RecyclerView 中重复拖动动画?

我正在使用 itemtouchhelper 类来支持在我的 RecyclerView 中拖放。当我在它周围拖动一个项目时,它会按预期在视觉上更新(交换行)。一旦我放下该项目,就会发生另一个 ** 视觉** 拖动。例如(见下图),如果我将项目“a”从索引 0 拖动到索引 3,正确的列表显示项目“b”现在位于索引 0。他们回收视图重复操作并在索引 0 处获取新项目("b") 并将其拖动到索引 3!无论我从哪个索引拖动到哪个索引,都会发生这种重复拖动。

我将其称为 **visual** 拖动,因为我提交给 RecyclerView 的 listadapter 的列表已正确排序(通过日志验证)。如果我重新启动我的应用程序,列表的顺序是正确的。或者,如果我调用 notifyDataSetChanged(),在不需要的动画之后,它会正确排序。是什么导致了第二个动画?

编辑:根据文档,如果您在 areContentsTheSame() 方法 (DiffUtil) 中使用 equals() 方法,“此处错误地返回 false 将导致动画过多。”据我所知,我在下面的 POJO 文件中正确覆盖了此方法。我被难住了...

Unexpected reordering of list

Second drag operation

MainActivity.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);
  }
}).attachToRecyclerView(recyclerMain);

RecyclerAdapterMain.java

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

  // Registers MainActivity as a listener to checkBox clicks. Main will update database accordingly.
  private CheckBoxListener checkBoxListener;

  public interface CheckBoxListener {
    void onCheckBoxClicked(ListItem item); // Method implemented in MainActivity
  }

  public void setCheckBoxListener(CheckBoxListener checkBoxListener) {
    this.checkBoxListener = checkBoxListener;
  }

  public RecyclerAdapterMain() {
    super(DIFF_CALLBACK);
  }

    // Static keyword makes DIFF_CALLBACK variable available to the constructor when it is called
    // DiffUtil will compare two objects to determine if updates are needed
    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();
    }

    // Documentation - NOTE: if you use equals,your object must properly override Object#equals().
    // Incorrectly returning false here will result in too many animations. 
    // As far as I can tell I am overriding the equals() properly in my POJO below
    @Override
    public boolean areContentsTheSame(@NonNull ListItem oldItem,@NonNull ListItem newItem) {
      return oldItem.equals(newItem);
    }
  };

  @NonNull
  @Override
  public ListItemHolder onCreateViewHolder(@NonNull ViewGroup parent,int viewType) {
    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.recycler_item_layout_main,parent,false);
    return new ListItemHolder(itemView);
  }

  @Override
  public void onBindViewHolder(@NonNull ListItemHolder holder,int position) {
    ListItem item = getItem(position);
    Resources resources = holder.itemView.getContext().getResources();
    holder.txtItemName.setText(item.getItemName());
    holder.checkBox.setChecked(item.getIsChecked());

    // Set the item to "greyed out" if checkBox is checked,normal color otherwise
    if(item.getIsChecked()) {
      holder.txtItemName.setTextColor(Color.LTGRAY);
      holder.checkBox.setButtonTintList(ColorStateList
          .valueOf(resources.getColor(R.color.checkedColor,null)));
    } else {
      holder.txtItemName.setTextColor(Color.BLACK);
      holder.checkBox.setButtonTintList(ColorStateList
          .valueOf(resources.getColor(R.color.uncheckedColor,null)));
    }
  }

  public class ListItemHolder extends RecyclerView.ViewHolder {
    private TextView txtItemName;
    private CheckBox checkBox;

    public ListItemHolder(@NonNull View itemView) {
      super(itemView);
      txtItemName = itemView.findViewById(R.id.txt_item_name);

      // Toggle checkBox state
      checkBox = itemView.findViewById(R.id.checkBox);
      checkBox.setonClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          checkBoxListener.onCheckBoxClicked(getItem(getAdapterPosition()));
        }
      });
    }
  }

  public ListItem getItemAt(int position) {
    return getItem(position);
  }
}

ListItem.java (POJO)

@Entity(tableName = "list_item_table")
public class ListItem {

  @PrimaryKey(autoGenerate = true)
  private long id;

  private String itemName;
  private boolean isChecked;
  private double positionInList;

  public long getId() {
    return id;
  }

  public void setId(long id) {
    this.id = id;
  }

  public String getItemName() {
    return itemName;
  }

  public void setItemName(String itemName) {
    this.itemName = itemName;
  }

  public void setChecked(boolean isChecked) {
    this.isChecked = isChecked;
  }

  public boolean getIsChecked() {
    return isChecked;
  }

  public void setPositionInList(double positionInList) {
    this.positionInList = positionInList;
  }

  public double getPositionInList() {
    return positionInList;
  }

  @Override
  public boolean equals(@Nullable Object obj) {
    ListItem item = new ListItem();
    if(obj instanceof ListItem) {
       item = (ListItem) obj;
    }

    return this.getItemName().equals(item.getItemName()) &&
        this.getIsChecked() == item.getIsChecked() &&
        this.getPositionInList() == item.getPositionInList();
  }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?