RecyclerView 上的项目删除没有动画
Posted
技术标签:
【中文标题】RecyclerView 上的项目删除没有动画【英文标题】:No animation on item removal on RecyclerView 【发布时间】: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_
android:layout_
android:minHeight="@dimen/standard_list_item_height"
android:paddingBottom="8dp"
android:background="@drawable/darker_background_on_pressed"
android:clickable="true">
......
</RelativeLayout>
我做错了什么导致默认删除动画不起作用?
【问题讨论】:
【参考方案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
。因此,在这些方法中,您可以更新空视图的可见性,并且不会破坏动画。
【讨论】:
以上是关于RecyclerView 上的项目删除没有动画的主要内容,如果未能解决你的问题,请参考以下文章
如何将 videoView 与 imageView 和 recyclerView 一起制作动画?
精通RecyclerView:打造ListViewGridView瀑布流;学会添加分割线 添加删除动画 Item点击事件