I have a problem regarding RecyclerView + Swipe to delete + undo with the help of a snackbar. Whenever I try to fast swipe 3+ items to delete them, viewHolder.getAdapterPosition();
returns a wrong value. Seems like it can't react that fast.
RecyclerView list Example:
So, if I quickly swipe away Test3, Test2 and then Test1 viewHolder.getAdapterPosition();
returns the positions 2, 2 and then 0.
The correct return has to be 2, 1 and then 0.
For two items everything behaves normal, returns 2 and then 1.
Note: Last position is always returned 2 seconds later, because snackbar times out and then the item gets deleted. The snackbars which show before get dismissed as soon as the next item gets swiped away.
Any ideas how to solve this? I tried to figure it out for several hours now.
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT) {
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
@Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, int swipeDir) {
final int position = viewHolder.getAdapterPosition();
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
rusure = Snackbar.make(fab, getResources().getString(R.string.rule_deleted), Snackbar.LENGTH_SHORT).addCallback(new Snackbar.Callback() {
@Override
public void onDismissed(Snackbar snackbar, int event) {
switch(event) {
case Snackbar.Callback.DISMISS_EVENT_ACTION:
rulesAdapter.notifyDataSetChanged();
break;
case Snackbar.Callback.DISMISS_EVENT_TIMEOUT:
case Snackbar.Callback.DISMISS_EVENT_CONSECUTIVE:
case Snackbar.Callback.DISMISS_EVENT_MANUAL:
Log.d("test", Integer.toString(position));
if(position!=-1 && position<arrayList.size()){
// Define 'where' part of query.
String selection = DatabaseTablesColums.FeedEntry._ID + " LIKE ?";
// Specify arguments in placeholder order.
String[] selectionArgs = {Integer.toString(arrayList.get(position).getId())};
// Issue SQL statement.
arrayList.remove(position);
rulesAdapter.notifyItemRemoved(position);
rulesAdapter.notifyItemRangeChanged(position, rulesAdapter.getItemCount());
db.delete(DatabaseTablesColums.FeedEntry.TABLE_NAME, selection, selectionArgs);
// Remove item from backing list here
}
break;
}
}
@Override
public void onShown(Snackbar snackbar) {
}
}).setAction(getResources().getString(R.string.undo), new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
rusure.show();
}
});
itemTouchHelper.attachToRecyclerView(recyclerView);
Looks like you are removing the item from the backing data store, in your case, it's the arrayList
and notifies the adapter only after the SnackBar dismisses.
So if you swipe the next item before the first Snackbar dismisses, RecyclerView adapter has no idea that the first item has been removed from the adapter. So it's giving you a wrong position.
You should remove the item from the backing data store (arrayList) and call notifyItemRemoved()
as soon as you swipe the item rather than updating after the Snackbar dismisses.
You can update the database only after the SnackBar dismisses, though.