RecyclerView Swipe To Delete

Arayüze sahip yazılımsal projelerde,  projenin  en az backend yazılımının iyi olması  kadar kullanıcısını doğru tanıyan, onların yapacağı eylemlerin kolay, hızlıca ve eğlenceli bir şekilde yapabilmesini sağlayan ekranların tasarlanması da çok önemlidir.

Bu makalemde, Gmail mail silme işleminde olduğu gibi, listelenen içeriklerin animasyonlu bir şekilde kaydırarak (swipe) silme işlemini ve silinen kaydın geri alınabilmesini sağlayan yapıyı örnekleyerek anlatacağım.

Örneğimizi uyguladığımızda aşağıdaki gibi bir ekran görüntüsünü elde edeceğiz.

Android Uygulamalarda Kaydırarak Silme (Swipe To Delete)

Uygulama tasarımınızda, listelenen kayıtları kaydırma animasyonu ile silmek istiyorsanız, ilk öncelikle arayüz kodlama da  RecyclerView elementini eklemelisiniz.

RecyclerView arayüz elementini işlevsel hale getirmek için de ItemTouchHelper yardımcı sınıfını kullanmanız gerekir.

Not: Proje kodlarında ItemTouchHelper sınıfıyla alakalı gerekli açıklamaları bulabilirsiniz.

Birkaç adımda RecyclerView widget ile Swipe To Delete özelliğini uygulamamızda  nasıl kullanacağımızı görelim.

1-Gerekli Kütüphanelerin Eklenmesi

Android Studio Ide ile oluşturduğum projemin app dizinin altındaki build.gradle dosyasını açıyoruz. Dependencies kod bloklarının arasına aşağıdaki kodları yerleştirerek  kütüphanelerimizi yükleyelim.

implementation 'com.android.support:appcompat-v7:28.0.0-rc02'
implementation 'com.android.support:design:28.0.0-rc01'

2-Arayüz Kodlaması

Custom Listview kullanımında olduğu gibi, burada da 2 tane xml dosyasında kodlarımız bulunmalıdır.

İlk olarak, listelenecek item’larda gösterilecek arayüz elemanlarının yerleştirileceği xml dosyasını oluşturmalıyız.

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

Diğer xml dosyamıza da RecyclerView widget’ını yerleştirmemiz gerekmektedir.

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

3-Java kodlama ile işlevsellik oluşturma

Aşağıdaki SwipeToDeleteCallback.java sınıfıdır. ItemTouchHelper.Callback sınıfından kalıtım alınmıştır. Bu sınıf, kaydırma animasyonunu ve kaydırma sonucunda oluşan görüntüyü sağlar.

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();
}
//Burada kaydırma yönünü belirledik. Yön işaretini makeMovementFlags statik yönteminde döndürdük.
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
return makeMovementFlags(0, ItemTouchHelper.LEFT);
}
//Bu metod sürükle ve bırak için kullanılır. Kullanmıcagımız için, false döndürdüm.
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
return false;
}
//Bu metodda kaydırmanın gerçekleştiğini gösteren özel görünümümüzü oluşturuyoruz.
@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);
}
//Burada float değerini döndürüyoruz. örnek 0.7f, RecyclerView satırındaki yüzde 70 kaydırma işleminin kaydırma olarak sayılacağı anlamına gelir.
@Override
public float getSwipeThreshold(@NonNull RecyclerView.ViewHolder viewHolder) {
return 0.7f;
}
}

Aşağıdaki RecyclerViewAdapter sınıfıdır. Item’ları tasarım anlamında özelleştirebilmek için oluşturulmuş bir sınıftır. Ek olarak,  restoreItem metodu sayesinde sildiğimiz kaydı geri alabilir, removeItem metodu sayesinde de item’i silebilirsiniz.

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();
}
//Item'i silme işlemi
public void removeItem(int position) {
data.remove(position);
notifyItemRemoved(position);
}
// Snackbar işlemi tıklandığında, restoreItem metodunu kullanarak öğeyi RecyclerView'da geri yükleriz.
public void restoreItem(String item, int position) {
data.add(position, item);
notifyItemInserted(position);
}
public ArrayList<String> getData() {
return data;
}
}

Aşağıdaki MainActivity sınıfıdır. Bu sınıfta listelenecek değerleri bir diziye aktarıp, RecyclerViewAdapter sınıfına gönderdim. Diğer bir yandan item’ı kaydırarak (swipe) sildiğinizde, Snackbar çıkmasını, silme ve silinen item’ın geri almasını sağlayan metodu kullandık.

 
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.util.Log;
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);
//silinecek item ın position bilgisiyle, removeItem kullanarak silme işlemi yapıldı
mAdapter.removeItem(position);
//Snackbar oluşturulması
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) {
//item ın position bilgisiyle, restoreItem kullanarak geri alma işlemi yapıldı
mAdapter.restoreItem(item, position);
recyclerView.scrollToPosition(position);
}
});
snackbar.setActionTextColor(Color.YELLOW);
snackbar.show();
}
};
ItemTouchHelper itemTouchhelper = new ItemTouchHelper(swipeToDeleteCallback);
itemTouchhelper.attachToRecyclerView(recyclerView);
}
}

Böylelikle projemizi oluşturmuş olduk.

Son olarak ufak bir not: Bu yukarıda anlattığım projemin kodlarını indirmek isterseniz; yapmanız gereken tek şey aşağıya koyduğum KODLARI İNDİR resmine tıklamak.

download