2014-11-28 20 views
42

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

я не устанавливать какой-либо пользовательский элемент аниматор, но в соответствии с 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_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> 

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

ответ

31

Решено.

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

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

+9

И индексы будут получать перепутались, если вы этого не сделаете. – milosmns

+0

то как исправить? – MobileMon

+3

Решение этой проблемы заключается в использовании viewHolder.getAdapterPosition() для onClick вместо позиции, переданной внутри onBindViewHolder – MobileMon

-1

я столкнулся же вопрос, и я это исправил, реализовав свою собственную 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() в mDataObserver. Таким образом, в этом методе вы можете обновлять видимость пустого представления и не прерывать анимацию.

43

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

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

спасибо, что это сработало для меня – commonSenseCode

3

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

в адаптере

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

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

6

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

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

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

5

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

+1

Это еще одна причина. Анимация вставки работает с 'wrap_content', но не с удалением. Любой способ обхода этого? –

2

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

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

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

Смежные вопросы