在RecyclerView中确认和撤消删除

che*_*riy 25 android android-recyclerview

我在RecyclerView中有一个简单项目列表.使用ItemTouchHelper实现"轻扫到删除"行为非常容易.

public class TripsAdapter extends RecyclerView.Adapter<TripsAdapter.VerticalItemHolder> {
    private List<Trip> mTrips;
    private Context mContext;
    private RecyclerView mRecyclerView;

    [...]

    //Let adapter know his RecyclerView. Attaching ItemTouchHelper
    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new TripItemTouchHelperCallback());
        itemTouchHelper.attachToRecyclerView(recyclerView);
        mRecyclerView = recyclerView;
    }

    [...]

    public class TripItemTouchHelperCallback extends ItemTouchHelper.SimpleCallback {
        public  TripItemTouchHelperCallback (){
            super(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT);
        }

        @Override
        public boolean onMove(RecyclerView recyclerView,
                              RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
            //some "move" implementation
        }
        @Override
        public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
            //AND WHAT HERE?
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

它运作良好.但是我还需要实现一些撤消操作或确认.做这个的最好方式是什么?

第一个问题是如何使用确认对话框插入另一个视图代替删除?如果用户选择撤消删除,如何恢复刷卡项目?

Jir*_*kaV 21

我同意@Gabor最好软删除项目并显示撤消按钮.

但是我正在使用Snackbar来展示UNDO.这对我来说更容易实现.

我正在将Adapter和RecyclerView实例传递给我的ItemTouchHelper回调.我的onSwiped很简单,大部分工作都是通过适配器完成的.

这是我的代码(编辑2016/01/10):

@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
    mAdapter.onItemRemove(viewHolder, mRecyclerView);
}
Run Code Online (Sandbox Code Playgroud)

适配器的onItemRemove方法是:

   public void onItemRemove(final RecyclerView.ViewHolder viewHolder, final RecyclerView recyclerView) {
    final int adapterPosition = viewHolder.getAdapterPosition();
    final Photo mPhoto = photos.get(adapterPosition);
    Snackbar snackbar = Snackbar
            .make(recyclerView, "PHOTO REMOVED", Snackbar.LENGTH_LONG)
            .setAction("UNDO", new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    int mAdapterPosition = viewHolder.getAdapterPosition();
                    photos.add(mAdapterPosition, mPhoto);
                    notifyItemInserted(mAdapterPosition);
                    recyclerView.scrollToPosition(mAdapterPosition);
                    photosToDelete.remove(mPhoto);
                }
            });
    snackbar.show();
    photos.remove(adapterPosition);
    notifyItemRemoved(adapterPosition);
    photosToDelete.add(mPhoto);
}
Run Code Online (Sandbox Code Playgroud)

photosToDelete是myAdapter的ArrayList字段.我在recyclelerView主机片段的onPause()方法中真正删除了这些项目.

注意编辑2016/01/10:

  • 改变了硬编码的位置@Sourabh在评论中提出
  • 有关RV的适配器和片段的完整示例,请参阅此要点


Gáb*_*bor 13

通常的方法是不要在刷卡时立即删除该项目.提出一条消息(它可能是一个小吃吧,或者像在Gmail中一样,覆盖刚刚刷过的项目的消息),并为消息提供超时和撤消按钮.

如果用户在消息可见时按下撤消按钮,您只需关闭该消息并返回正常处理.仅在超时超过而没有用户按下撤销按钮的情况下删除实际项目.

基本上,这些方面的东西:

@Override
public void onSwiped(final RecyclerView.ViewHolder viewHolder, int direction) {
  final View undo = viewHolder.itemView.findViewById(R.id.undo);
  if (undo != null) {
    // optional: tapping the message dismisses immediately
    TextView text = (TextView) viewHolder.itemView.findViewById(R.id.undo_text);
    text.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        callbacks.onDismiss(recyclerView, viewHolder, viewHolder.getAdapterPosition());
      }
    });

    TextView button = (TextView) viewHolder.itemView.findViewById(R.id.undo_button);
    button.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        recyclerView.getAdapter().notifyItemChanged(viewHolder.getAdapterPosition());
        clearView(recyclerView, viewHolder);
        undo.setVisibility(View.GONE);
      }
    });

    undo.setVisibility(View.VISIBLE);
    undo.postDelayed(new Runnable() {
      public void run() {
        if (undo.isShown())
          callbacks.onDismiss(recyclerView, viewHolder, viewHolder.getAdapterPosition());
      }
    }, UNDO_DELAY);
  }
}
Run Code Online (Sandbox Code Playgroud)

这假设undo项目视图中存在布局,通常是不可见的,有两个项目,一个文本(说明已删除或类似)和一个撤消按钮.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

  ...

  <LinearLayout
      android:id="@+id/undo"
      android:layout_width="match_parent"
      android:layout_height="match_parent"
      android:background="@android:color/darker_gray"
      android:orientation="horizontal"
      android:paddingLeft="10dp"
      android:paddingRight="10dp"
      android:visibility="gone">
    <TextView
        android:id="@+id/undo_text"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="2"
        android:gravity="center|start"
        android:text="Deleted"
        android:textColor="@android:color/white"/>
    <TextView
        android:id="@+id/undo_button"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center|end"
        android:text="UNDO"
        android:textColor="?attr/colorAccent"
        android:textStyle="bold"/>
  </LinearLayout>
</FrameLayout>
Run Code Online (Sandbox Code Playgroud)

点击按钮只会删除消息.(可选)点击文本确认删除并通过调用代码中的相应回调立即删除项目.别忘了回电话给你的适配器notifyItemRemoved():

public void onDismiss(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, int position) {
  //TODO delete the actual item in your data source
  adapter.notifyItemRemoved(position);
}
Run Code Online (Sandbox Code Playgroud)

  • 我试图实现该解决方案,但在显示撤消按钮时,任何单击都会再次调用onSwiped()方法而不是onClick().就像没有完全删除行一样,撤消视图上的任何触摸事件都被解释为滑动.有任何想法吗? (4认同)