web-dev-qa-db-fra.com

Aucune animation sur la suppression d'éléments sur RecyclerView

J'utilise RecyclerView pour la première fois. Tout fonctionne bien sauf qu'il n'y a pas d'animation sur la suppression d'éléments, même si l'animation sur l'ajout d'éléments fonctionne parfaitement.

Je n'ai pas défini d'animateur d'élément personnalisé, mais selon la documentation :

Les animations pour l'ajout et la suppression d'éléments sont activées par défaut dans RecyclerView.

Donc, les animations sur la suppression devraient fonctionner.

J'aimerais que l'animation par défaut soit supprimée, mais je ne parviens pas à la faire fonctionner.

Voici comment j'ai configuré le 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);
}

Ceci est mon adaptateur:

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'est-ce que je fais de mal qui fait que l'animation de suppression par défaut ne fonctionne pas?

53
Jordi Chacón

La méthode appropriée pour supprimer un élément de la vue du recycleur consiste à supprimer l'élément du jeu de données, puis à indiquer à l'adaptateur que l'élément est supprimé,

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

Résolu.

Le problème était que, après avoir appelé mAdapter.remove(position), une autre partie de mon code appelait mAdapter.notifyDataSetChanged(), ce qui, je suppose, arrête l'animation de suppression.

En résumé, si vous appelez mAdapter.notifyDataSetChanged pendant qu’une animation est en cours, celle-ci s’arrête.

34
Jordi Chacón

Utilisez notifyItemRemoved(position) au lieu de notifyDataSetChanged() comme ci-dessous

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

parce que notifyDataSetChanged() notifie simplement les données mises à jour sans aucune animation.

11
Praveen Balaji

Une autre raison pour laquelle une animation de suppression ne fonctionne pas correctement peut être la hauteur RecyclerViews Vérifiez que la hauteur est match_parent et NOT wrap_content!

5
user1185087

J'ai pu supprimer la vue avec l'animation et les index mis à jour comme suit:

Dans l'adaptateur,

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

Tout en supprimant les vues, appelez

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

J'ai utilisé une méthode booléenne pour m'assurer que les doubles clics, etc. ne provoque pas de crash.

5
mUser1990

après un long débogage, j'ai réalisé que je devais ajouter setHasStableIds(true) à mon adaptateur et le mettre en œuvre.

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

après que supprimer l'animation a commencé à travailler

3
netimen

En retard mais pourrait être utile à quelqu'un qui veut {rechercher des éléments avec animation}

Utilisez le code ci-dessous dans votre activité ou fragment où 

yourAdapter.animateTo(filteredModelList);

Utilisez le code ci-dessous dans votre classe 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);
    }
0
Adeeb karim