Android Swipe to Delete RecyclerView items with UNDO

The ItemTouchHelper class added in Android Support Library V7 can be used to delete an item of RecyclerView by swiping the item with undo functionality. Or in other words, we can create a swipe to delete the items of RecyclerView, using the ItemTouchHelper class. To configure the events to be performed to swipe or move the item of the RecyclerView, the ItemTouchHelper class has a SimpleCallback class.

ItemTouchHelper class:

To swipe to dismiss and drag & drop the items of RecyclerView, the ItemTouchHelper class is used which is a utility class. Depending on the functionality to be implemented, the ItemTouchHelper class overrides the onMove() or onSwipe() callback methods.

Example:

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://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>

cardview_row.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://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>

MainActivity.java:

In the MainActivity.java file, we will write the below code.

package com.example.radioapp;
 
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("Student 1");
        stringArrayList.add("Student 2");
        stringArrayList.add("Student 3");
        stringArrayList.add("Student 4");
        stringArrayList.add("Student 5");
        stringArrayList.add("Student 6");
        stringArrayList.add("Student 7");
        stringArrayList.add("Student 8");
        stringArrayList.add("Student 9");
        stringArrayList.add("Student 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);
    }
 
 
}

SwipeToDeleteCallback.java:

In the SwipeToDeleteCallback.java file, we will extend the ItemTouchHelper.SimpleCallback class. We will also override its onMove(), onSwiped(), onChildDraw() methods. To perform the swipe operation on an item, we will call the onSwiped() method. To include the implementation logic of drawing canvas while swiping the items of RecyclerView, the onChildDraw() methods are called.

package com.example.radioapp;
 
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.mipmap.ic_launcher_round);
        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;
    }
}

RecyclerViewAdapter.java:

In the RecyclerViewAdapter.java file, we will extend the RecyclerView.Adapter class and will override its onCreateViewHolder(), onBindViewHolder() methods.

package com.example.radioapp;
 
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;
    }
}

Output 1:

Output 2:

Output 3:

Output 4: