Reputation: 11040
My task is to implement item horizontal item swiping in RecyclerView. I'm actually done with the task thanks to ViewPager source code and some other resources, but have problems with one scenario.
My SwipeableRecyclerView (SRV from now on) extends RecyclerView and implements RecyclerView.OnItemTouchListener to perform the magic, which actually works. It also defines a custom interface, SwipeListener, which defines a few methods, most important (for this question) is 'onSwipe(View view, int position, boolean right)'.
My activity supplies an implementation of the interface to the SRV, and when a swipe is detected and the listener method fired, it does the following:
A problem sometimes occurs when I swipe very quickly, as in while the first animation is not done yet, I start swiping another row. Sometimes, it creates 'holes' / phantom rows with no view; sometimes, I get the following exception:
java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling
and the stack trace shows that the adapter.notifyItemRemoved() is the method causing the exception.
For the exception problem, the sequence of events is the following (from my debug traces):
started for position: 11
started for position: 10
finished for position: 11
removing 11
removed 11
finished position: 10
removing 10
So, both finishing animations are started one after another, and when one of them removed its item and called notifyItemRemoved, the other one finished and called removing the item. I'm not really sure how this can be as all of it happens on the main thread, actually, but I guess the scrolling also is done in animation frames, and it somehow with the new calculations needed because of the second removal.
So, I'm in a pickle, I have no idea what to do about this one. Actually, I think the easiest thing should be to disable touches of the whole recycler view when an animation is started, and enable them again only after the scrolling was done. I fail to disable the SRV, though - I tried setEnabled(false), clickable, etc. but nothing works.
Any help?
Upvotes: 4
Views: 4922
Reputation: 25194
You can check for mRecyclerView.getItemAnimator().isRunning()
, a bit easier than the current answer. While true
, MotionEvent
s should not trigger any removal.
Upvotes: 4
Reputation: 11040
Here is what eventually worked for me:
add this code in the method which is responsible for handling the swipe gesture (like removing the entry etc.):
swipeableRecyclerView.setSwipingEnabled(false);
add this code near where the SRV is created:
swipeableRecyclerView.setItemAnimator(new DefaultItemAnimator() {
@Override
public void onRemoveFinished(RecyclerView.ViewHolder item) {
swipeableRecyclerView.setSwipingEnabled(true);
}
});
This just extends the default item animator with an implementation of a method which is called when the remove animation is finished.
Now, my swipe-to-dismiss is 'serialized' and no exceptions occur.
Upvotes: 2