Подтвердить что ты не робот

Нет анимации при удалении элементов на RecyclerView

Я использую RecyclerView в первый раз. Все работает отлично, за исключением того, что нет анимации при удалении элементов, даже если анимация при добавлении элементов работает просто отлично.

Я не задал ни одного настраиваемого элемента аниматора, но в соответствии с документацией:

Анимация для добавления и удаления элементов включена по умолчанию в 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>

Что я делаю неправильно, что приводит к тому, что анимация удаления по умолчанию не работает?

4b9b3361

Ответ 1

Правильный способ удаления элемента из представления recycler состоит в том, чтобы удалить элемент из набора данных, а затем сообщить адаптеру, что элемент удален таким образом

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

Ответ 2

Решил.

Проблема заключалась в том, что после вызова mAdapter.remove(position) другая часть моего кода вызывала mAdapter.notifyDataSetChanged(), который, как я полагаю, останавливает анимацию удаления.

Подводя итог, если вы вызываете mAdapter.notifyDataSetChanged, пока анимация продолжается, анимация останавливается.

Ответ 3

Используйте notifyItemRemoved(position) вместо notifyDataSetChanged(), как показано ниже

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

потому что notifyDataSetChanged() просто уведомляет обновленные данные без каких-либо анимаций.

Ответ 4

Другой причиной неэффективной обработки удаленной анимации может быть высота RecyclerViews. Убедитесь, что высота match_parent и NOT wrap_content!

Ответ 5

Мне удалось удалить представление с анимацией и обновленными индексами следующим образом:

Внутри адаптера

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());
}

Я использовал логический метод для обеспечения двойного щелчка и т.д. не вызывают сбоев.

Ответ 6

после долгой отладки я понял, что мне пришлось добавить setHasStableIds(true) к моему адаптеру и реализовать

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

после этого удаленная анимация начала работать

Ответ 7

Я столкнулся с одной и той же проблемой, и я исправил это, реализовав мой собственный 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 visibility
    }

}

В принципе, когда вы добавляете/удаляете элемент в/из recyclerview, вы можете вызывать notifyItemInserted()/notifyItemRemoved() и notifyItemRangeChanged(), этот метод будет вызывать onItemRangeRemoved()/onItemRangeInserted() в mDataObserver. Таким образом, в этом методе вы можете обновлять видимость видимого вида и не прерывать анимацию.