Android RecyclerView Swipe To Delete & Undo

Android RecyclerView Swipe To Delete & Undo – ItemTouchHelper Class

Using RecyclerView to perform swipe to Delete & Undo is uses the ItemTouchHelper utility class. If you have read the article on using ItemTouchHelper to create a Drag and Drop feature using RecyclerView, this post will be easy to follow. The majority of the heavy load work is done by the ItemTouchHelper Class. ItemTouchHelper.Callback acts as a bridge between the RecyclerView and the UI motions that you perform.

There are several actions that this utility class captures like, Swipe, Move, ChildDraw, Draw Canvas etc. You can see more about the utility in creating a Swipe to Delete & Undo RecyclerView below.

YOU ARE YOUR BOSS
I agree to have my personal information transfered to MailChimp ( more information )
Join over 5.000 visitors who are receiving our free content and learn how to program with Android. The growth of Android is estimated to reach a whooping 83% by 2020. Hop on the train and start making money!
We hate spam. Your email address will not be sold or shared with anyone else.

Creating the Layout

The first objective is to create a simple layout that will be holding the RecyclerView. We have made use of the Coordinator Layout as our Root layout. This is because we cannot use the relative layout to hold the Recyclerview and also at the same time bind a SnackBar to it. This is bad practice since there will not be the support to auto move the elements in the RecyclerView.

<?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">

    <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>

Create ListItems layout

We are done with the main layout. But the RecyclerView should be holding a list of items. We will use the list_items.xml layout file to create the item list.

Root layout is going to be a CardView, which will help us create a better-looking item list. See the final output to understand the reason for the item attributes.

<?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>

Creating an Adapter for RecyclerView

If you had read the post about using the RecyclerView and classItemTouchHelper.Callback to create a Drag and Drop feature in Android, you will know that we are in need of an Adapter to dynamically hold our elements. This is done as below. Create RecyclerAdapter.java with the following code

package com.monks.android.newapplication;

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 RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.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 RecyclerAdapter(ArrayList<String> data) {
        this.data = data;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_items, 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;
    }
}


This is a pretty straightforward template class structure for creating a custom adapter. However, we additionally make use of 2 methods removeItem() and restoreItem(). 

This method will be called by our next important class, which inherits the ItemTouchHelper.Callback

Creating UIActionClass inheriting ItemTouchHelper.Callback

Time to create our UIAction class that will inherit the ItemTouchHelper.Callback to enable us to perform Swipe action on the RecyclerView items.

We will make use of the list_items.xml layout file to insert a delete icon which will show up on swiping left.

To understand better, see the code below

package com.monks.android.newapplication;


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 UIActionClass extends ItemTouchHelper.Callback {

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


    UIActionClass(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, android.R.drawable.ic_menu_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;
    }
}


There are a lot of things that are happening in the above class. Stepwise statements are below.

  • The 4 main callbacks which we use here are getMovementFlags(), onChildDraw(), onSwipeThreshold() and clearCanvas()
  • The getMovementFlags() callback gets fired when the user swipes on the RecyclerView. When we set the makeMovementFlags() and call ItemTouchHelper.LEFT.
  • onChildDraw() takes care of performing the swiping action and painting the rest of the screen space red. NOTE: The ItemTouchHelper by default will not remove any items, it merely creates an action of swiping.
  • clearCanvas() like the name suggests clears the item from the canvas.

RecyclerActivity to perform Swipe to Delete and Undo

The final activity to stitch all the components above is created in the RecyclerActivity. The main activities performed are,

  • Creating an Adapter object and passing it to the RecyclerAdapter
  • We create an onSwipeEnabled() method, to detect the swiping action with the UIActionClass object.
package com.monks.android.newapplication;

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 RecyclerActivity extends AppCompatActivity {


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

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

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

        stringArrayList.add("AndroidMonks");
        stringArrayList.add("TextView");
        stringArrayList.add("Buttons");
        stringArrayList.add("EditText");
        stringArrayList.add("ImageView");
        stringArrayList.add("Relative Layout");
        stringArrayList.add("Linear Layout");

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

        enableSwipeToDeleteAndUndo();


    }

    private void enableSwipeToDeleteAndUndo() {
        UIActionClass swipeToDeleteCallback = new UIActionClass(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);
    }
}

Upon finishing these classes, run this in your AVD.

Also Read  CheckBox in Android - Implementation and Overview

Leave a Comment

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