Reputation: 16399
I have a RecyclerView
showing a list of items. The ViewHolder
for each rows contains a delete button, to remove the row. If I remove the last row and tap very fast on the disappearing row, I can crash my app because the second tap event is delivered to the removed row. I'm surprised that android delivers the second event. Before I try adding something like boolean isDeleted
to my ViewHolder
subclass, I'm wondering: am I doing something else wrong to get in this situation?
class MyAdapter extends RecyclerView.Adapter<MyViewHolder>
implements ItemTouchHelperAdapter {
List<Segment> segments;
MyAdapter(List<Segment> objs) {
this.segments = objs;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LinearLayout v = (LinearLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.segment_edit_row, parent, false);
MyViewHolder vh = new MyViewHolder(v);
return vh;
}
@Override
public void onBindViewHolder(MyViewHolder holder, int position) {
Segment seg = segments.get(position);
holder.textView.setText(seg.getTitle());
holder.dragHandle.setOnTouchListener((v, event) -> {
if (MotionEventCompat.getActionMasked(event) ==
MotionEvent.ACTION_DOWN) {
onStartDrag(holder);
}
return false;
});
holder.deleteButton.setOnClickListener(v -> {
segments.remove(position);
notifyItemRemoved(position);
});
View.OnClickListener editExerciseListener = v -> {
Segment segment = segments.get(position);
startEditSegmentActivity(segment, position);
};
holder.textView.setOnClickListener(editExerciseListener);
holder.arrow.setOnClickListener(editExerciseListener);
}
The deleteButton
handler runs first, and then the editExerciseListener
, with the position
that is now out of bounds.
Update
Several people have suggested I call notifyDataSetChanged
. The Android docs specifically recommend not to do that if you can describe your change with a call to notifyItemRemoved
instead.
Are those docs wrong?
Upvotes: 0
Views: 165
Reputation: 23
I ran into similar situation a while ago. The app crashed when I clicked recyclerview item while it was being removed and was undergoing removal animation (takes less than a second). The duration for which your app is vulnerable to a potential crash is very small in this situation.
WHAT I DID :
I logged the value of position (holder.getAdapterPosition()) when the app crashed. Surprisingly, every time the crash occurred, the value was -1. Eventually, I wrote a custom version of notifyItemRemoved. Here it is:
private void notifyItemRemovedModified(int pos_removed){
if(pos_removed!=-1){
// your list is your list
yourList.remove(pos_removed);
notifyItemRemoved(pos_removed);
}
}
Upvotes: 0
Reputation: 704
First call notifyItemRemoved(position);
then call segments.remove(position);
Upvotes: 0
Reputation: 645
Update your code as:
View.OnClickListener editExerciseListener = v -> {
if(position >= segments.size()){
//index not exists
}else{
// index exists
Segment segment = segments.get(position);
startEditSegmentActivity(segment, position);
}
};
Upvotes: 0
Reputation: 167
You have to reload recyclerview every you perform delete operation: You can do by adding the following line:
yourAdapter.notifyDataSetChanged();
Upvotes: 2