【问题标题】:RecyclerView row flashes/blinks when item inserted插入项目时 RecyclerView 行闪烁/闪烁
【发布时间】:2020-12-04 00:58:56
【问题描述】:

每当我将一个项目插入到我的 Room 数据库中时,我的回收器视图都会闪烁包含新插入的视图持有者的行(列表的其余部分不会闪烁)。我正在使用 LiveData 通过在观察到的 ViewModel 方法的 onChanged() 中调用 submitList(list) 来自动更新列表。我的适配器扩展了 ListAdapter,我正在使用 DiffUtil 来跟踪列表中的更改。话虽如此,我不会直接调用 notifyItemInserted(position) ,因为 DiffUtil 应该为我执行此操作。有 2 个实例会插入一个项目 (1) 一个全新的项目被插入到列表的末尾 (2) 一个已删除的项目被重新插入到列表中。在这两种情况下,项目都会自行插入然后闪烁。我读过很多帖子,人们建议在回收站视图上禁用动画,但这对我来说不是一个选择,因为我依赖代码中其他地方的动画。任何其他建议将不胜感激。我试图保持发布的代码简短,但如果有帮助,我可以发布更多内容。

MainActivity.java

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

@Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    initComponents();
    initRecyclerView();
    setListeners();
    setListObserver();
    createItemTouchHelper();
  }

  private void setListObserver() {
    viewModel.getAllItems().observe(this, new Observer<List<ListItem>>() {
      @Override
      public void onChanged(List<ListItem> newList) {
        adapterMain.submitList(newList);
      }
    });
  }

...

// Inserts a new ListItem when MainActivity's EditText is used
  public void onClick(View v) {
    if (v.getId() == R.id.img_add_item_main) {
        String itemName = String.valueOf(edtAddItem.getText());
        if (!itemName.trim().equals("")) { // Insert new list item only if the EditText is not empty
          ListItem item = new ListItem();
          item.setItemName(itemName);
          viewModel.insert(item);
        }

...

// SnackBar to allow a user to undo a delete operation
  public void showUndoSnackBar(ListItem deletedItem) {
    Snackbar undoSnackBar = Snackbar.make(constraintLayout, "Undo deleted Item",
        Snackbar.LENGTH_LONG).setAction("UNDO", new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        // Restore deleted item to its original position in the list if UNDO is clicked
        viewModel.insert(deletedItem);
      }
    });
    undoSnackBar.show();
  }

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);
    }

@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);

    holder.txtItemName.setText(item.getItemName());
    holder.checkBox.setChecked(item.getIsChecked());

    if(item.getIsChecked()) {
      holder.txtItemName.setTextColor(Color.LTGRAY);
    } else {
      holder.txtItemName.setTextColor(Color.BLACK);
    }
  }

...

ListItem.java (POJO)

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

  @PrimaryKey(autoGenerate = true)
  private long id;

  private String itemName;
  private boolean isChecked;

  public long getId() {
    return id;
  }

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

  ... other getters and setters

  public boolean equals(@Nullable ListItem listItem) {
    return this.itemName.equals(listItem.getItemName()) && this.isChecked == listItem.getIsChecked();
  }
}

【问题讨论】:

    标签: java android-recyclerview android-room android-livedata android-diffutils


    【解决方案1】:

    我确定 DefaultItemAnimator 类(在撰写本文时是 RecyclerView 的动画器)有一个名为 animateAdd(final RecyclerView.ViewHolder holder) 的方法,它将持有者的 alpha 设置为 0,然后随着时间的推移将其设置为 1。我通过将默认设置更改为 1 来验证这是导致闪烁的原因。我通过结合使用 StackOverflow post 中接受的答案和 DefaultItemAnimator 的 documentation 来解决问题。

    首先,我创建了一个扩展 DefaultItemAnimator 的新动画器类,以覆盖 animateAdd() 方法。在 animateAdd() 部分下,文档指出,“在将项目添加到 RecyclerView 时调用。实现者可以选择 是否 以及如何为该更改设置动画,但必须始终调用 dispatchAddFinished(RecyclerView.ViewHolder ) 完成后,立即(如果不会出现动画)或在动画实际完成后。"我立即调用了 dispatchAddFinished() 以避免添加动画。除了有问题的动画之外,所有动画都存在,而不是完全禁用动画。

    MyRecyclerViewAnimator.java

    public class MyRecyclerViewAnimator extends DefaultItemAnimator {
    
      @Override
      public boolean animateAdd(RecyclerView.ViewHolder holder) {
        dispatchAddFinished(holder); // this is what bypasses the animation
        return true;
      }
    
    /* this is the default implementation of animateAdd() in DefaultItemAnimator
    @Override
        public boolean animateAdd(final RecyclerView.ViewHolder holder) {
            resetAnimation(holder);
            holder.itemView.setAlpha(0); // this is what caused the flashing/blinking
            mPendingAdditions.add(holder);
            return true;
        }
    */
    }
    

    MainActivity.java

    ...
    
      recyclerMain.setItemAnimator(new MyRecyclerViewAnimator()); // set the default animator to your extended class
    
    ...
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-10
      • 1970-01-01
      • 1970-01-01
      • 2017-03-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多