アンドロイドのリサイクラービューでスワイプして削除し、取り消す
このチュートリアルでは、AndroidアプリケーションのRecyclerViewでのスワイプして削除する機能の説明と実装について取り上げます。
アンドロイドのスワイプで削除
スワイプして削除する機能は、RecyclerViewから行を削除するためによく使用されます。スワイプして削除機能を実装するためには、ItemTouchHelperユーティリティクラスを使用する必要があります。
アイテムタッチヘルパーのコールバック
ItemTouchHelperクラスを使用するためには、ItemTouchHelper.Callbackを実装する必要があります。ItemTouchHelper.Callbackクラスは、主にドラッグアンドドロップやスワイプして削除する動作に使用されます。このチュートリアルでは、スワイプして削除のみを使います。Androidフレームワークは、ItemTouchHelper.Callbackの基本的な実装であるSimpleCallbackを提供しています。自分自身でスワイプして削除の機能を実装します。以下は、クラス内でオーバーライドする必要のある主要なメソッドです。
– getMovementFlags:スワイプの方向を設定します。方向フラグは、静的メソッドmakeMovementFlagsで返されます。
– onMove:ドラッグアンドドロップに使用されます。必要なければここではfalseを返します。
– onSwiped:スワイプアクションが検出されたときにトリガされます。完全なスワイプは画面の全幅に行きます。部分的なスワイプをスワイプとして考慮するには、以下のメソッドをオーバーライドする必要があります。
– getSwipeThreshold:浮動小数点値を返します。例えば0.5fは、RecyclerViewの行の50%のスワイプがスワイプとして考慮されることを意味します。
– onChildDraw:スワイプが起こっていることを示すカスタムビューを作成します。
ItemTouchHelper.Callbackは、単に行をスワイプするために使用されます。それ自体は削除しません。RecyclerViewアダプターを使用して自分で削除する必要があります。
十分な話し合いです。私たちはコードをしましょう。次のセクションでは、スワイプして削除できるRecyclerViewを含むAndroidアプリを作成します。私たちは元に戻すオプションを備えたSnackbarを提供します。
プロジェクトの構造
アプリのbuild.gradleに次の依存関係を追加してください。
implementation 'com.android.support:design:28.0.0-rc01'
コード
以下にactivity_main.xmlのレイアウトのコードが示されています。
<?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>
以下に、ItemTouchHelper.Callbackクラスを拡張するSwipeToDeleteCallback.javaクラスのコードが示されています。
package com.scdev.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;
}
}
onSwipeメソッドが実装されていないため、クラスは抽象的です。それはMainActivity.javaクラスで実装します。onChildDrawメソッド内で、isCanceledブール値を使用してスワイプが行われたかどうかをチェックします。その結果に基づいて、削除アイコンを持つビューを作成します。スワイプの閾値を0.7に設定しています。つまり、行が70%未満でスワイプされた場合、onSwipeメソッドはトリガーされません。MainActivity.javaクラスのコードは以下の通りです。
package com.scdev.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);
}
}
RecyclerViewにItemTouchHelperを設定するためには、attachToRecyclerViewメソッドを使用します。Snackbarのアクションがクリックされたときには、restoreItemメソッドを使用して、RecyclerView中のアイテムを復元します。restoreItemメソッドは、RecyclerViewAdapterクラスで定義されています。scrollToPositionは、指定された位置までRecyclerViewをスクロールします。これは、RecyclerViewの先頭にアイテムが挿入された場合に主に使用されます。cardview_row.xmlのコードは以下の通りです。
<?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>
以下にRecyclerViewAdapter.javaクラスのコードが示されています。
package com.scdev.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;
}
}
上記のアプリケーションの出力結果は以下の通りです。これでチュートリアルは終了です。プロジェクトは以下のリンクからダウンロードできます。
アンドロイドのリサイクラービューでスワイプして削除する
ギットハブプロジェクトのリンク