【问题标题】:No animation on item removal on RecyclerViewRecyclerView 上的项目删除没有动画
【发布时间】:2015-01-27 01:24:06
【问题描述】:

我是第一次使用RecyclerView。一切正常,除了没有关于移除项目的动画,即使添加项目的动画效果很好。

我没有设置任何自定义的item animator,而是根据documentation

RecyclerView 中默认启用添加和删除项目的动画。

所以删除动画应该可以工作。

我希望在删除时使用默认动画,但无法使其正常工作。

这就是我设置 RecyclerView 的方式:

private void setupRecyclerView() {
  mRecyclerView = (RecyclerView) mRootView.findViewById(R.id.recycler_view);
  mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
  View emptyView = mRootView.findViewById(R.id.empty_view);
  mAdapter = new RoutineAdapter(getActivity(), mRoutineItems, emptyView);
  mRecyclerView.setAdapter(mAdapter);
}

这是我的适配器:

private class RoutineAdapter
      extends RecyclerView.Adapter<RoutineAdapter.ViewHolder> {

private final Context mContext;
private List<RoutineItem> mData;
private View mEmptyView;

    public RoutineAdapter(Context context, List<RoutineItem> data, View emptyView) {
      mContext = context;
      mData = data;
      mEmptyView = emptyView;
      setEmptyViewVisibility();
    }

    public void add(RoutineItem routineItem, int position) {
      mData.add(position, routineItem);
      setEmptyViewVisibility();
      notifyItemInserted(position);
    }

    public void remove(int position){
      mData.remove(position);
      setEmptyViewVisibility();
      notifyItemRemoved(position);
    }

    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
      final View view = LayoutInflater.from(mContext).inflate(
          R.layout.fragment_routines_list_item, parent, false);
      return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
      final RoutineItem routineItem = getItem(position);
      holder.circle.setBackgroundResource(
          colorNumberToDrawableResource(routineItem.colorNumber));
      holder.initial.setText(routineItem.routineName.substring(0, 1));
      holder.routineName.setText(routineItem.routineName);
      holder.lastTimeDone.setText(routineItem.lastTimeDoneText);
      if (routineItem.isSelected) {
        holder.itemView.setBackgroundColor(
            getResources().getColor(R.color.background_item_selected));
      } else {
        holder.itemView.setBackgroundResource(
            R.drawable.darker_background_on_pressed);
      }
      holder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          mPresenter.onRoutineClicked(routineItem.routineName);
        }
      });
      holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
          mPresenter.onRoutineLongClicked(routineItem.routineName);
          return true;
        }
      });
    }

    @Override
    public int getItemCount() {
      return mData.size();
    }

    public RoutineItem getItem(int position) {
      return mData.get(position);
    }

    private void setEmptyViewVisibility() {
      if (getItemCount() == 0) {
        mEmptyView.setVisibility(View.VISIBLE);
      } else {
        mEmptyView.setVisibility(View.GONE);
      }
    }

    class ViewHolder extends RecyclerView.ViewHolder {
      public final View circle;
      public final TextView initial;
      public final TextView routineName;
      public final TextView lastTimeDone;

      public ViewHolder(View view) {
        super(view);
        circle = view.findViewById(R.id.circle);
        initial = (TextView) view.findViewById(R.id.initial);
        routineName = (TextView) view.findViewById(R.id.routine_name);
        lastTimeDone = (TextView) view.findViewById(R.id.last_time_done);
      }
    }
}

Fragment_routines_list_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:minHeight="@dimen/standard_list_item_height"
  android:paddingBottom="8dp"
  android:background="@drawable/darker_background_on_pressed"
  android:clickable="true">
    ......
</RelativeLayout>

我做错了什么导致默认删除动画不起作用?

【问题讨论】:

    标签: android android-recyclerview


    【解决方案1】:

    从回收器视图中删除项目的正确方法是从数据集中删除该项目,然后告诉适配器该项目已被删除

    myDataset.remove(position); // myDataset is List<MyObject>
    mAdapter.notifyItemRemoved(position);
    

    【讨论】:

      【解决方案2】:

      解决了。

      问题是,在调用 mAdapter.remove(position) 之后,我的代码的另一部分调用了 mAdapter.notifyDataSetChanged(),我认为这会停止删除动画。

      总而言之,如果您在动画正在进行时调用mAdapter.notifyDataSetChanged,动画将停止。

      【讨论】:

      • 如果你不这样做,索引会搞砸的。
      • 解决这个问题的方法是使用 viewHolder.getAdapterPosition() 进行 onClick,而不是在 onBindViewHolder 内部传入位置
      【解决方案3】:

      使用notifyItemRemoved(position) 代替notifyDataSetChanged(),如下所示

      myDataset.remove(position);
      notifyItemRemoved(position);
      

      因为notifyDataSetChanged() 只是简单地通知更新的数据而没有任何动画。

      【讨论】:

      • 但是适配器会丢失列表中其他项目的位置,当你尝试删除另一个项目时,它会删除它下面的项目,如果它下面没有项目,那么它会崩溃。请确认。
      • myDataset.remove(position); notifyItemRemoved(位置); notifyItemRangeChanged(位置, youDataSets.size());使用此代码,这将解决您的问题。
      【解决方案4】:

      我能够删除带有动画和更新索引的视图,如下所示:

      在适配器内,

      public boolean removeItem(int position) {
          if (data.size() >= position + 1) {
              data.remove(position);
              return true;
          }
          return false;
      }
      

      删除视图时,调用

      if (adapter.removeItem(position)) {
          adapter.notifyItemRemoved(position);
          adapter.notifyItemRangeChanged(position, adapter.getItemCount());
      }
      

      我使用了布尔方法来确保双击等。不要导致崩溃。

      【讨论】:

        【解决方案5】:

        经过长时间调试,我意识到我必须将setHasStableIds(true) 添加到我的适配器并实现

        @Override
        public long getItemId(int position) {
            return position;
        }
        

        之后移除动画开始起作用

        【讨论】:

          【解决方案6】:

          移除动画无法正常工作的另一个原因可能是RecyclerViews 高度。验证高度是match_parent 而不是wrap_content

          【讨论】:

          • 这是另一个原因。插入动画适用于wrap_content,但不适用于删除动画。有什么办法可以解决这个问题?
          【解决方案7】:

          迟到但可能对想要{搜索带有动画的项目}的人有所帮助

          在您的活动或片段中使用以下代码

          yourAdapter.animateTo(filteredModelList);
          

          在您的 RecyclerAdapter 类中使用以下代码

          public void animateTo(List<CommonModel> models) {
                  applyAndAnimateRemovals(models);
                  applyAndAnimateAdditions(models);
                  applyAndAnimateMovedItems(models);
              }
          
              private void applyAndAnimateRemovals(List<CommonModel> newModels) {
                  for (int i = items.size() - 1; i >= 0; i--) {
                      final CommonModel model = items.get(i);
                      if (!newModels.contains(model)) {
                          removeItem(i);
                      }
                  }
              }
          
              private void applyAndAnimateAdditions(List<CommonModel> newModels) {
                  for (int i = 0, count = newModels.size(); i < count; i++) {
                      final CommonModel model = newModels.get(i);
                      if (!items.contains(model)) {
                          addItem(i, model);
                      }
                  }
              }
          
              private void applyAndAnimateMovedItems(List<CommonModel> newModels) {
                  for (int toPosition = newModels.size() - 1; toPosition >= 0; toPosition--) {
                      final CommonModel model = newModels.get(toPosition);
                      final int fromPosition = items.indexOf(model);
                      if (fromPosition >= 0 && fromPosition != toPosition) {
                          moveItem(fromPosition, toPosition);
                      }
                  }
              }
          
              private CommonModel removeItem(int position) {
                  final CommonModel model = items.remove(position);
                  notifyItemRemoved(position);
                  return model;
              }
          
              private void addItem(int position, CommonModel model) {
                  items.add(position, model);
                  notifyItemInserted(position);
              }
          
              private void moveItem(int fromPosition, int toPosition) {
                  final CommonModel model = items.remove(fromPosition);
                  items.add(toPosition, model);
                  notifyItemMoved(fromPosition, toPosition);
              }
          

          【讨论】:

            【解决方案8】:

            我遇到了同样的问题,我通过实现自己的 RecyclerView 解决了这个问题,在我的 recyclerview 中,我这样做了:

            public class MyRecyclerView extends RecyclerView {
               private View mEmptyView;
               private AdapterDataObserver mDataObserver = new AdapterDataObserver() {
                   public void onChanged() {
                      super.onChanged();
                      updateEmptyView();
                   }
            
                   public void onItemRangeRemoved(int positionStart, int itemCount) {
                       super.onItemRangeRemoved(positionStart, itemCount);
                       updateEmptyView();
                   }
            
                   public void onItemRangeInserted(int positionStart, int itemCount) {
                      super.onItemRangeInserted(positionStart, itemCount);
                      updateEmptyView();
                   }
                 };
            
                // private void setAdapter() {}
            
                private void updateEmptyView() {
                     // update empty view's visibility
                }
            
            }
            

            基本上,当您在recyclerview中添加/删除项目时,您可以调用notifyItemInserted()/ notifyItemRemoved()和notifyItemRangeChanged(),这些方法将调用onItemRangeRemoved() / onItemRangeInserted() in mDataObserver。因此,在这些方法中,您可以更新空视图的可见性,并且不会破坏动画。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 2019-04-12
              • 1970-01-01
              • 1970-01-01
              • 2020-09-25
              • 2018-04-25
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多