ventajas usar studio son recyclerview qué que personalizado los libreria elementos ejemplo cuando construyen cardview android android-recyclerview

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