Is there a way to revert a swipe action and restore the view holder to its initial position after the swipe is completed and onSwiped
is called
After some random poking I found a solution. Call notifyItemChanged
on you adapter. This will make the swiped out view animate back into it's original position.
You should override onSwiped
method in ItemTouchHelper.Callback
and refresh that particular item.
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder,
int direction) {
adapter.notifyItemChanged(viewHolder.getAdapterPosition());
}
Call notifyDataSetChanged on your adapter to make the swipe back work consistent
In the case of using LiveData
to provide a list to a ListAdapter
, calling notifyItemChanged
does not work. However, I found a fugly workaround which involves re-attaching the ItemTouchHelper
to the recycler view in onSwiped
callback as such
val recyclerView = someRecyclerViewInYourCode
var itemTouchHelper: ItemTouchHelper? = null
val itemTouchCallback = object : ItemTouchHelper.Callback {
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction:Int) {
itemTouchHelper?.attachToRecyclerView(null)
itemTouchHelper?.attachToRecyclerView(recyclerView)
}
}
itemTouchHelper = ItemTouchHelper(itemTouchCallback)
itemTouchHelper.attachToRecyclerView(recyclerView)
onSwiped never call, always revert
override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
return 1f
}
override fun getSwipeEscapeVelocity(defaultValue: Float): Float {
return Float.MAX_VALUE
}
Google's ItemTouchHelper
implementation assumes that every swiped out item will eventually get removed from the recycler view, whereas it might not be the case in some applications.
RecoverAnimation
is a nested class in ItemTouchHelper
that manages the touch animation of the swiped/dragged items. Although the name implies that it only recovers the position of items, it's actually the only class that is used to recover (cancel swipe/drag) and replace (move out on swipe or replace on drag) items. Strange naming.
There's a boolean property named mIsPendingCleanup
in RecoverAnimation
, which ItemTouchHelper
uses to figure out whether the item is pending removal. So ItemTouchHelper
, after attaching a RecoverAnimation
to the item, sets this property after a successful swipe out, and the animation does not get removed from the list of recover animations as long as this property is set. The problem is that, mIsPendingCleanup
will always be set for a swiped out item, causing the RecoverAnimation
for the item to never be removed from the list of animations. So even if you recover the item's position after a successul swipe, it will be sent back to the swiped-out position as soon as you touch it - because the RecoverAnimation will cause the animation start from the latest swiped-out position.
Solution to this is unfortunately to copy the ItemTouchHelper
class source code into the same package as it is in the support library, and remove the mIsPendingCleanup
property from the RecoverAnimation
class. I'm not sure if this is acceptable by Google, and I haven't posted the update to Play Store yet to see whether it will cause a reject, but you may find the class source code from support library v22.2.1 with the above mentioned fix at https://gist.github.com/kukabi/f46e1c0503d2806acbe2.