Tutorial

Android RecyclerView Swipe To Delete And Undo

Published on August 3, 2022
author

Anupam Chugh

Android RecyclerView Swipe To Delete And Undo

In this tutorial, we’ll be discussing and implementing the Swipe to Delete feature on the RecyclerView in our Android Application.

Android Swipe To Delete

Swipe to delete feature is commonly used to delete rows from a RecyclerView. In order to implement Swipe to delete feature, we need to use the ItemTouchHelper utility class.

ItemTouchHelper.Callback

In order to use ItemTouchHelper class, we need to implement the ItemTouchHelper.Callback. ItemTouchHelper.Callback class is used primarily for drag and drop and swipe to delete behaviors. In this tutorial, we’ll be sticking with the swipe to delete only. Android framework provides us with a basic implementation of ItemTouchHelper.Callback which is SimpleCallback. We will be creating our own implementation of the Swipe to delete feature. Following are the major methods that need to be overridden in our class: getMovementFlags - Here we set the direction of swipe. We return the direction flag in a static method makeMovementFlags. onMove - This is used for drag and drop. If not needed, return false here. onSwiped - This gets triggered when the swipe action is detected. A complete swipe goes the full width of the screen. In order to set a consider a partial swipe as a swipe we need to override the following method: getSwipeThreshold - Here we return the float value. example 0.5f means that a 50 percent swipe on the RecyclerView row would be considered as a swipe. onChildDraw - Here we’ll create our custom view that shows that the swipe is happening.

ItemTouchHelper.Callback is used to just swipe the rows. It doesn’t delete them itself. We’ll need to delete it ourself using the RecyclerView Adapter.

Enough Talk. Let’s code. In the next section, we’ll be creating our android application with a RecyclerView containing the Swipe to Dismiss feature. We’ll provide a Snackbar with the undo option.

Project Structure

android recyclerview swipe to delete Add the following dependency in the app’s build.gradle:

implementation 'com.android.support:design:28.0.0-rc01'

Code

The code for the activity_main.xml layout is given below:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:id="@+id/coordinatorLayout"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        app:layoutManager="android.support.v7.widget.LinearLayoutManager" />


    </RelativeLayout>

</android.support.design.widget.CoordinatorLayout>

The code for the SwipeToDeleteCallback.java class which extends the ItemTouchHelper.Callback class is given below:

package com.journaldev.androidrecyclerviewswipetodelete;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View;

abstract public class SwipeToDeleteCallback extends ItemTouchHelper.Callback {

    Context mContext;
    private Paint mClearPaint;
    private ColorDrawable mBackground;
    private int backgroundColor;
    private Drawable deleteDrawable;
    private int intrinsicWidth;
    private int intrinsicHeight;


    SwipeToDeleteCallback(Context context) {
        mContext = context;
        mBackground = new ColorDrawable();
        backgroundColor = Color.parseColor("#b80f0a");
        mClearPaint = new Paint();
        mClearPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
        deleteDrawable = ContextCompat.getDrawable(mContext, R.drawable.ic_delete);
        intrinsicWidth = deleteDrawable.getIntrinsicWidth();
        intrinsicHeight = deleteDrawable.getIntrinsicHeight();
        

    }


    @Override
    public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
        return makeMovementFlags(0, ItemTouchHelper.LEFT);
    }

    @Override
    public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
        return false;
    }

    @Override
    public void onChildDraw(@NonNull Canvas c, @NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);

        View itemView = viewHolder.itemView;
        int itemHeight = itemView.getHeight();

        boolean isCancelled = dX == 0 && !isCurrentlyActive;

        if (isCancelled) {
            clearCanvas(c, itemView.getRight() + dX, (float) itemView.getTop(), (float) itemView.getRight(), (float) itemView.getBottom());
            super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
            return;
        }

        mBackground.setColor(backgroundColor);
        mBackground.setBounds(itemView.getRight() + (int) dX, itemView.getTop(), itemView.getRight(), itemView.getBottom());
        mBackground.draw(c);

        int deleteIconTop = itemView.getTop() + (itemHeight - intrinsicHeight) / 2;
        int deleteIconMargin = (itemHeight - intrinsicHeight) / 2;
        int deleteIconLeft = itemView.getRight() - deleteIconMargin - intrinsicWidth;
        int deleteIconRight = itemView.getRight() - deleteIconMargin;
        int deleteIconBottom = deleteIconTop + intrinsicHeight;


        deleteDrawable.setBounds(deleteIconLeft, deleteIconTop, deleteIconRight, deleteIconBottom);
        deleteDrawable.draw(c);

        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);


    }

    private void clearCanvas(Canvas c, Float left, Float top, Float right, Float bottom) {
        c.drawRect(left, top, right, bottom, mClearPaint);

    }

    @Override
    public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
        return 0.7f;
    }
}

The class is abstract since we haven’t implemented the onSwipe method. We’ll do that in the MainActivity.java class. Inside the onChildDraw method we check if the swipe is done or not using the isCancelled boolean. Based on that we create a view with delete icon. We’ve set the Swipe threshold to 0.7. That means if the row is swiped less than 70%, the onSwipe method won’t be triggered. The code for the MainActivity.java class is given below:

package com.journaldev.androidrecyclerviewswipetodelete;

import android.graphics.Color;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.CoordinatorLayout;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.view.View;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {


    RecyclerView recyclerView;
    RecyclerViewAdapter mAdapter;
    ArrayList<String> stringArrayList = new ArrayList<>();
    CoordinatorLayout coordinatorLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = findViewById(R.id.recyclerView);
        coordinatorLayout = findViewById(R.id.coordinatorLayout);

        populateRecyclerView();
        enableSwipeToDeleteAndUndo();


    }

    private void populateRecyclerView() {
        stringArrayList.add("Item 1");
        stringArrayList.add("Item 2");
        stringArrayList.add("Item 3");
        stringArrayList.add("Item 4");
        stringArrayList.add("Item 5");
        stringArrayList.add("Item 6");
        stringArrayList.add("Item 7");
        stringArrayList.add("Item 8");
        stringArrayList.add("Item 9");
        stringArrayList.add("Item 10");

        mAdapter = new RecyclerViewAdapter(stringArrayList);
        recyclerView.setAdapter(mAdapter);


    }

    private void enableSwipeToDeleteAndUndo() {
        SwipeToDeleteCallback swipeToDeleteCallback = new SwipeToDeleteCallback(this) {
            @Override
            public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {

                
                final int position = viewHolder.getAdapterPosition();
                final String item = mAdapter.getData().get(position);

                mAdapter.removeItem(position);


                Snackbar snackbar = Snackbar
                        .make(coordinatorLayout, "Item was removed from the list.", Snackbar.LENGTH_LONG);
                snackbar.setAction("UNDO", new View.OnClickListener() {
                    @Override
                    public void onClick(View view) {
                        
                        mAdapter.restoreItem(item, position);
                        recyclerView.scrollToPosition(position);
                    }
                });

                snackbar.setActionTextColor(Color.YELLOW);
                snackbar.show();

            }
        };

        ItemTouchHelper itemTouchhelper = new ItemTouchHelper(swipeToDeleteCallback);
        itemTouchhelper.attachToRecyclerView(recyclerView);
    }


}

In order to set the ItemTouchHelper onto the RecyclerView, the attachToRecyclerView method is used. When the Snackbar action is clicked we restore the item in the RecyclerView using the restoreItem method. The restoreItem method is defined in the RecyclerViewAdapter class. scrollToPosition scrolls the RecyclerView to the position specified. This is mainly used when an item is inserted at the top of the RecyclerView. The code for the cardview_row.xml is given below:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:card_view="https://schemas.android.com/apk/res-auto"
    android:id="@+id/cardView"
    android:layout_width="match_parent"
    android:layout_height="64dp"
    android:layout_margin="8dp"
    card_view:cardCornerRadius="0dp"
    card_view:cardElevation="2dp">


    <RelativeLayout
        android:id="@+id/relativeLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="8dp"
        android:paddingLeft="8dp"
        android:paddingRight="8dp">


        <TextView
            android:id="@+id/txtTitle"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:text="Item 1"
            android:textAppearance="@style/TextAppearance.Compat.Notification.Title" />
    </RelativeLayout>

</android.support.v7.widget.CardView>

The code for the RecyclerViewAdapter.java class is given below:

package com.journaldev.androidrecyclerviewswipetodelete;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.List;

public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder> {

    private ArrayList<String> data;

    public class MyViewHolder extends RecyclerView.ViewHolder {

        private TextView mTitle;
        RelativeLayout relativeLayout;

        public MyViewHolder(View itemView) {
            super(itemView);

            mTitle = itemView.findViewById(R.id.txtTitle);
        }
    }

    public RecyclerViewAdapter(ArrayList<String> data) {
        this.data = data;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.cardview_row, parent, false);
        return new MyViewHolder(itemView);
    }

    @Override
    public void onBindViewHolder(MyViewHolder holder, int position) {
        holder.mTitle.setText(data.get(position));
    }

    @Override
    public int getItemCount() {
        return data.size();
    }


    public void removeItem(int position) {
        data.remove(position);
        notifyItemRemoved(position);
    }

    public void restoreItem(String item, int position) {
        data.add(position, item);
        notifyItemInserted(position);
    }

    public ArrayList<String> getData() {
        return data;
    }
}


The output of the above application in action is given below: android recyclerview swipe to delete output This brings an end to this tutorial. You can download the project from the link below:

AndroidRecyclerViewSwipeToDelete

Github Project Link

Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.

Learn more about our products

About the authors
Default avatar
Anupam Chugh

author

While we believe that this content benefits our community, we have not yet thoroughly reviewed it. If you have any suggestions for improvements, please let us know by clicking the “report an issue“ button at the bottom of the tutorial.

Still looking for an answer?

Ask a questionSearch for more help

Was this helpful?
 
JournalDev
DigitalOcean Employee
DigitalOcean Employee badge
January 31, 2021

Thank you, very good tutorial. It works perfectly. NOTE: If you are using a newer version of Android Studio, inside the “SwipeToDeleteCallback(Context context)” method, you need to include : super(ItemTouchHelper.LEFT, ItemTouchHelper.RIGHT); because you are extending the ItemTouchHelper.SimpleCallback class and you need to call its constructor with the code above.

- The Great Alexander 99

    JournalDev
    DigitalOcean Employee
    DigitalOcean Employee badge
    November 12, 2020

    How to add text delete below delete icon

    - Gaurav

      JournalDev
      DigitalOcean Employee
      DigitalOcean Employee badge
      September 26, 2020

      Is it working in a recycler view with firebase?

      - Iqbal

        JournalDev
        DigitalOcean Employee
        DigitalOcean Employee badge
        May 12, 2020

        Thank you so much, clear code with some explanation that make easy implement AND understanding this thing, in order to also learn something

        - Giovanni

          JournalDev
          DigitalOcean Employee
          DigitalOcean Employee badge
          February 20, 2020

          Is the above functionality possible with two object lists in recycler view?

          - Himanshu

            JournalDev
            DigitalOcean Employee
            DigitalOcean Employee badge
            March 14, 2019

            Hi Anupam, your tutorial works great for me!, save me a lot of time, i disabled the part of the undo part because the logic of my app impact into db, but was a great turorial for me. thank you

            - victor

              JournalDev
              DigitalOcean Employee
              DigitalOcean Employee badge
              March 10, 2019

              Hi Anupam, Thanks for your tutorial. It is clear and working. However, when I try to implement this feature to one of the fragment of my Bottom Navigation app, it clash. Do you know the reason or it can only be used in activity? Please help.

              - Girio Wong

                JournalDev
                DigitalOcean Employee
                DigitalOcean Employee badge
                October 18, 2018

                Hi, after the deletion, the snackbar with the text and the undo button did not appear. how do i fix that problem

                - yj

                  Try DigitalOcean for free

                  Click below to sign up and get $200 of credit to try our products over 60 days!

                  Sign up

                  Join the Tech Talk
                  Success! Thank you! Please check your email for further details.

                  Please complete your information!

                  Become a contributor for community

                  Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.

                  DigitalOcean Documentation

                  Full documentation for every DigitalOcean product.

                  Resources for startups and SMBs

                  The Wave has everything you need to know about building a business, from raising funding to marketing your product.

                  Get our newsletter

                  Stay up to date by signing up for DigitalOcean’s Infrastructure as a Newsletter.

                  New accounts only. By submitting your email you agree to our Privacy Policy

                  The developer cloud

                  Scale up as you grow — whether you're running one virtual machine or ten thousand.

                  Get started for free

                  Sign up and get $200 in credit for your first 60 days with DigitalOcean.*

                  *This promotional offer applies to new accounts only.