In this tutorial, we’ll be discussing and implementing the Swipe to Delete feature on the RecyclerView in our Android Application.
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.
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.
Add the following dependency in the app’s build.gradle:
implementation 'com.android.support:design:28.0.0-rc01'
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: This brings an end to this tutorial. You can download the project from the link below:
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
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.
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
How to add text delete below delete icon
- Gaurav
Is it working in a recycler view with firebase?
- Iqbal
Thank you so much, clear code with some explanation that make easy implement AND understanding this thing, in order to also learn something
- Giovanni
Is the above functionality possible with two object lists in recycler view?
- Himanshu
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
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
Hi, after the deletion, the snackbar with the text and the undo button did not appear. how do i fix that problem
- yj