Android RecyclerView滑动即可删除和撤消
在本教程中,我们将在Android应用程序中的RecyclerView上讨论并实现"刷卡删除"功能。
Android轻扫即可删除
滑动删除功能通常用于从RecyclerView中删除行。
为了实现"滑动删除"功能,我们需要使用ItemTouchHelper实用工具类。
ItemTouchHelper.Callback
为了使用ItemTouchHelper类,我们需要实现ItemTouchHelper.Callback
。
ItemTouchHelper.Callback类主要用于拖放和滑动以删除行为。
在本教程中,我们将坚持只将其删除。
Android框架为我们提供了ItemTouchHelper.Callback的基本实现,即SimpleCallback。
我们将创建自己的"滑动以删除"功能的实现。
以下是在我们的类中需要重写的主要方法:
getMovementFlags
–这里我们设置滑动方向。
我们以静态方法" makeMovementFlags"返回方向标志。
" onMove" –用于拖放。
如果不需要,请在此处返回false。
" onSwiped" –检测到轻扫动作时触发。
完全滑动即可进入屏幕的整个宽度。
为了将部分刷卡设置为刷卡,我们需要覆盖以下方法:
getSwipeThreshold
–这里返回浮点值。
示例0.5f表示将RecyclerView行上的50%刷卡视为刷卡。
" onChildDraw" –其中我们将创建自定义视图,以显示正在进行滑动。
ItemTouchHelper.Callback用于仅滑动行。
它不会删除它们本身。
我们需要使用RecyclerView适配器将其删除。
在下一部分中,我们将使用RecyclerView创建包含Swipe to Dismiss功能的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.theitroad.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方法中,我们使用isCancelled布尔值检查是否完成了滑动。
基于此,我们创建一个带有删除图标的视图。
我们已将"滑动"阈值设置为0.7。
也就是说,如果该行的滑动次数少于70%,则不会触发onSwipe方法。
MainActivity.java类的代码如下:
package com.theitroad.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); } }
为了将ItemTouchHelper设置到RecyclerView上,使用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.theitroad.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; } }