RecyclerView “swipe to delete”. No 3rd party lib necessary.

Author is a professional software developer providing android and iOS development services. More info here.

Checkout the code at the github repo.

Big shout-out to the guys creating libs for all the stuff Google promotes but doesn’t give us code for just yet, but the fact of the matter is I prefer not to use those libs unless I absolutely have to. The lib you include in your project might be doing a lot more than you need it for and with that comes a burden on your precious method limit count, possibly longer builds, another lib you and your coworkers need to keep in your heads…

The other day I had to implement “swipe to delete” feature. I wasn’t very happy about that since I feel that it’s not very discoverable and that those kind of gesture based actions shouldn’t be the only way to access the feature, but that’s another story. Still remembering how difficult this was couple of years back on a ListView I’ve started googling and soon enough there was a simple SO post with a simple solution and the best part – no 3rd party lib needed! However, it wasn’t to be so easy in the end.

First issue I had was what happens after the swipe? Sure I’d like to draw something below the swiping view, but once the view is swiped and the deed is done, it was kind of anticlimactic :/
issue 1

I wanted just to draw red background over empty space as items are animating to their new positions but I couldn’t do it inside the ItemTouchHelper class. I had to introduce another ItemDecorator in order to do so. The results were satisfying.

issue 1 fix

I was curious if I could easily implement the “undo” option, similar to the one GMail app provides. I had an idea how to do it and sure enough it worked!
undo
But all the time I was seeing a weird issue: When I would swipe delete the row, scroll around and come back, the row above the previously deleted row would be gone. Debugging showed that it was x translated off screen, as a result of the swiping as it turns out. Note how I swipe delete Item 2 but afterwards Item 1 is gone!
issue 2
I’ve sunk several hours into this issue, actually more than I’d like to admit. During that time I found several hacks to make this go away but no proper solution. In the end turns out it’s a regression bug in a recyclerview-v7 support library and the bug is gone in the newest version 3.1.1.

That’s all folks!

Be Sociable, Share!

24 thoughts on “RecyclerView “swipe to delete”. No 3rd party lib necessary.”

  1. Nice post, and thanks for the sample code!
    Have you tested the scenario where you swipe to delete, and then close the Activity after one second? In that case, because there is a 3 second delay, if the Activity gets destroyed (and thus the Adapter also gets destroyed) I assume the delete never actually happens.
    I guess you would need some extra code in the onPause of the Activity to tell the adapter to remove everything asap.

  2. Great article! The only thing i am having trouble with is that after swiping the UNDO button is also swipeable, so a user can swipe it off too. How can i fix it?

  3. @Kevin
    I have not tested that scenario. There is actually nothing to delete here since the adapter just creates some strings to display but in reality it would depend on the implementation. Are you calling an API do delete something or are you deleting it from the local DB? Either it won’t be deleted or if it would you’d have to manage your memory carefully and not hold the activity and the views. This is not production ready code, it’s just a sample code to give you an idea how to implement something like this.

    1. @Nemanja Kovačević
      Works great! Thanks a lot.
      I am experiencing a little problem with the undo layout size. My undo is in a LinearLayout that i hide and show. I want it to be the same size as the item that is swiped.

  4. I am trying to run this app in android studio 1.5 and its giving me error “launcher failed” any one facing the same??
    Also can any one suggest some tips on this issue.

  5. Hello! Did you try to change the color of your icon to xMark.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_ATOP). It’s overlapping row then. And drawing on top of it instead under it. It probably not visible with white color

  6. Hi,
    First of all great sample and article. It is great to finally see a swipe option with native tools and not with 3rd party libs. I was wondering if I could use the code you posted on github on my commercial app.
    Regards

  7. Hey! I’ve been using this example! It’s pretty useful. I’d like to know how do I get to make the red background swipeable to the right, so I can undo the delete this way too.

    My goal: When item is swiped to left, you get the garbage icon. You select the icon so as to delete the item. Swipe the red background to the right if you don’t want to remove the item.

  8. Hi! Thanks for the post, this was really useful. I wish you could help me or at least guide me with this problem. I’m having trouble with something that has to be with “drawables”. I’m getting a java.lang.ClassCastException: android.graphics.drawable.ColorDrawable cannot be cast to android.support.v7.widget.RoundRectDrawableWithShadow, but I’m not using any cardviews on my reyclerview. Do you have any idea what could it be? Thanks again

  9. This code is terrific! I do have one issue with it that I’m not sure how to solve. It seems that when I swipe on a list item (my list contains rather complex multiline items), and delete it, then further down in my list (off the visible view), items suddenly have their background turned red. Is there a solution for this?

  10. I spoke too soon…it seems I missed the bit of code in the onBindViewHolder method that set the background color if itemsPendingRemoval didn’t contain the item in question. It’s working fine now.

  11. Hey. When an item is removed from the recyclerview, if there is any other view, above and below this recyclerview, the animation looks weird. i.e, any view below this recyclerview comes up, after which the items of the recyclerview rearrange. This wont happen in your case as you have set fixed height for recyclerview. However I cannot set the height to fixed as i have a lot of views, and i have to change the recycler view height upon removal of items. PLease help

  12. Thanks for the simple and easy tutorial. I got a question that what to do if I want to make swipe only half way and take user’s input to delete or modify? I means add more choice on swipe.

  13. I have created a _very simple_ lib/module with your code (tho I have slightly refactored to remove most features I didn’t need). It’s located here: https://github.com/Gryzor/SwipeNoLib

    I provided Builder()s to be able to attach it. All it allows you to do is swipe left or right, no undo, no fanciness; posting this here in case anyone wants a 1-class solution that can be either copied and pasted or just simply added with very little code.

    Thanks for sharing this, it helped me figure out how to do it. :)

  14. First of all, great code! Thank you very much,
    In my scenario I implemented also searchView for recycler, and I’m doing list filtering from searchView input data, problem is that when I do filtering there is red bg for items, which is not needed in this scenario.

    I think that after :
    if (parent.getItemAnimator().isRunning()) {
    …RED bg
    }

    your code thinks that this is deleting animation, but in fact this is filtering animation, and red bg looks kinda wrong.
    Any suggestion how to resolve this?

  15. Great post, it helped me to understand in an easy way how to do the swipe to delete! Thanks for sharing it.
    I also managed to add an “onClick” to the itemView (keeping the swipe) after reading several posts on internet, but I’m not sure if it was the best option. So, I would like your comments on it.

    I added to the RecyclerView.Adapter an interface:

    class AlertListAdapter extends RecyclerView.Adapter {

    public interface OnItemClickListener {
    public void onItemClick(View view, int position, String id);
    }

    public void SetOnItemClickListener(final OnItemClickListener
    mItemClickListener) {
    this.mItemClickListener = mItemClickListener;
    }

    And then I implemented ViewGroup.OnClickListener at RecyclerView.ViewHolder:

    class AlertListHolder extends RecyclerView.ViewHolder
    implements ViewGroup.OnClickListener {

    public AlertListHolder(ViewGroup parent) {

    itemView.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
    mItemClickListener.onItemClick(v, getAdapterPosition(),
    this.titleTextView.getText().toString());
    }
    }

    In the activity that calls the adapter:

    public class MainActivity extends AppCompatActivity {
    AlertListAdapter oAlertListAdapter;
    RecyclerView mRecyclerView;

    protected void onCreate(Bundle savedInstanceState) {

    mRecyclerView = (RecyclerView) findViewById(R.id.mainListView);
    oAlertListAdapter = new AlertListAdapter();

    oAlertListAdapter.SetOnItemClickListener(
    new AlertListAdapter.OnItemClickListener() {
    @Override
    public void onItemClick(View view, int position, String id) {
    }
    });
    }
    }

    Is it the best option?

Leave a Reply

Your email address will not be published. Required fields are marked *