android - usar - No hay animación en la eliminación de elementos en RecyclerView
son los elementos qué construyen un recyclerview (8)
La forma correcta de eliminar un elemento de la vista del reciclador es eliminar el elemento del conjunto de datos y luego decirle al adaptador que el elemento se elimina de esa manera
myDataset.remove(position); // myDataset is List<MyObject>
mAdapter.notifyItemRemoved(position);
Estoy usando
RecyclerView
por primera vez.
Todo funciona bien, excepto que no hay animación en la eliminación de elementos, aunque la animación en la adición de elementos funciona bien.
No he configurado ningún animador de elementos personalizados, pero de acuerdo con la documentation :
Las animaciones para agregar y eliminar elementos están habilitadas de forma predeterminada en
RecyclerView
.
Entonces las animaciones sobre la eliminación deberían funcionar.
Me gustaría tener la animación predeterminada en la eliminación, pero no puedo hacer que funcione.
Así es como configuro el 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);
}
Este es mi adaptador:
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>
¿Qué estoy haciendo mal que hace que la animación de eliminación predeterminada no funcione?
Me encontré con el mismo problema, y lo solucioné implementando mi propio RecyclerView, y en mi vista de reciclador, hice esto:
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
}
}
Básicamente, cuando agrega / elimina un elemento en / de la vista de reciclado, puede llamar a notifyItemInserted () / notifyItemRemoved () y notifyItemRangeChanged (), este método invocará
onItemRangeRemoved()
/
onItemRangeInserted()
en
mDataObserver
.
Entonces, en este método, puede actualizar la visibilidad de la vista vacía y no interrumpirá las animaciones.
Otra razón para una animación de eliminación que no funciona correctamente podría ser la altura de
RecyclerViews
.
¡Verifique que la altura sea
match_parent
y NO
wrap_content
!
Pude eliminar la vista con la animación y los índices actualizados de la siguiente manera:
Dentro del adaptador,
public boolean removeItem(int position) {
if (data.size() >= position + 1) {
data.remove(position);
return true;
}
return false;
}
Mientras elimina las vistas, llame
if (adapter.removeItem(position)) {
adapter.notifyItemRemoved(position);
adapter.notifyItemRangeChanged(position, adapter.getItemCount());
}
He usado un método booleano para asegurar que haga doble clic, etc. No provoques un choque.
Resuelto.
El problema era que, después de llamar a
mAdapter.remove(position)
, otra parte de mi código estaba llamando a
mAdapter.notifyDataSetChanged()
que supongo que detiene la animación de eliminación.
En resumen, si llama a
mAdapter.notifyDataSetChanged
mientras hay una animación en curso, la animación se detendrá.
Tarde, pero podría ser útil para alguien que quiera {buscar elementos con animación}
Use el siguiente código en su actividad o fragmento donde
yourAdapter.animateTo(filteredModelList);
Use el siguiente código en su clase 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);
}
Use
notifyItemRemoved(position)
lugar de
notifyDataSetChanged()
como se muestra a continuación
myDataset.remove(position);
notifyItemRemoved(position);
porque
notifyDataSetChanged()
simplemente notifica los datos actualizados sin ninguna animación.
después de una larga depuración me di cuenta de que tenía que agregar
setHasStableIds(true)
a mi adaptador e implementar
@Override
public long getItemId(int position) {
return position;
}
después de eso, la animación de eliminación comenzó a funcionar