Android RecyclerView滑动即可删除和撤消

时间:2020-02-23 14:29:12  来源:igfitidea点击:

在本教程中,我们将在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;
  }
}