RecyclerView 条目拖拽横滑删除之 ItemTouchHelper 的使用

RecyclerView 之所以比 ListView 好用点,是因为它有一些强大的功能,比如 item 的拖拽或者横滑删除等功能,如果用 ListView 的话,这些需要我们去自定义,但是对于 RecyclerView 来说,系统已经为我们提供了方法,我们只需要按照 API 来调用即可。我们来看看 ItemTouchHelper 这个类。

ItemTouchHelper 从名字中判断,是条目触摸的辅助类,看它的注释说明是辅助item拖拽或滑动的类,我们先来实现一个简单的适配器


public class SwipeDragAdapter extends RecyclerView.Adapter<SwipeDragAdapter.SwipePressViewHolder> {

    private Context mContext;
    private ArrayList<String> mList;

    public SwipeDragAdapter(Context context, ArrayList<String> list) {

        this.mContext = context;
        this.mList = list;
    }

    @Override
    public SwipePressViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.item_swipe_press_rv, viewGroup, false);
        return new SwipePressViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final SwipePressViewHolder holder, int position) {

        String url = mList.get(position);
        holder.tvContent.setText(position + "        " + url);

    }

    @Override
    public int getItemCount() {
        int count = 0;
        if (mList != null && !mList.isEmpty()) {
            count = mList.size();
        }
        return count;
    }

    class SwipePressViewHolder extends RecyclerView.ViewHolder {

        private TextView tvContent;
        private ImageView ivDrag;

        SwipePressViewHolder(View itemView) {
            super(itemView);

            tvContent = (TextView) itemView.findViewById(R.id.tv_swipe_press);
            ivDrag = (ImageView) itemView.findViewById(R.id.iv_drag_swipe_press);
        }

    }

}

item 的 xml 布局

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <TextView
        android:id="@+id/tv_swipe_press"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_alignBottom="@+id/iv_drag_swipe_press"
        android:layout_marginEnd="16dp"
        android:layout_marginStart="16dp"
        android:gravity="center_vertical"
        android:lines="2"
        android:textColor="@color/color_3"
        android:textSize="14sp" />

    <ImageView
        android:id="@+id/iv_drag_swipe_press"
        android:layout_width="56dp"
        android:layout_height="56dp"
        android:layout_alignParentRight="true"
        android:contentDescription="@null"
        android:padding="16dp"
        android:src="@mipmap/ic_launcher"
         />
</RelativeLayout>

Activity 中 

    String[] arr = {
            " 1 ",
            " 2 ",
            " 3 ",
            " 4 ",
            " 5 ",
            " 6 ",
            " 7 ",
            " 8 ",
            " 9 ",
            " 10 ",
            " 11 ",
            " 12 ",
            " 13 ",
            " 14 ",
            " 15 "
    };
 
  
    LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
    recyclerView.setLayoutManager(linearLayoutManager);
    ArrayList<String> list = new ArrayList<>(Arrays.asList(arr));
    SwipeDragAdapter swipeDragAdapter = new SwipeDragAdapter(this, list);
    recyclerView.setAdapter(swipeDragAdapter);

这样,就出现了一个简单的列表。继续,ItemTouchHelper 中有对外暴露的回调,那么我们就写一个类来实现它的回调

public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {

    public SimpleItemTouchHelperCallback() {

    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        return 0;
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {

        return false;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

    }

}

实现默认的方法,我们看看这三个方法的名字及功能,getMovementFlags() 是获取移动标识的,我们可以在这个里面控制拖拽滑动的方向,比如说允许上下拖拽,不允许左右拖拽;onMove() 标识是否拖拽成功,可以在返回true的同时,对外暴露回调方法,我们在里面完成自己的逻辑操作;onSwiped() 这个很明显就是左右滑动的方法,我们可以在这个方法中对外暴露回调,删除集合中的元素,刷新界面。

除了上述三个方法,还有几个也要注意, isLongPressDragEnabled() 方法表示是否允许拖拽,这个是开关,默认为 true,只有为true才可以拖拽;isItemViewSwipeEnabled()方法表示是否允许横滑,使用方法同上;onSelectedChanged() 方法是在 item 被拖拽或滑动时调用;clearView() 方法对应 onSelectedChanged() 方法,它是指手指抬起时调用。

getMovementFlags() 中获取值,使用位运算的方式来记录数据的,ItemTouchHelper 本身提供了方向的int值,然后通过 makeMovementFlags() 方法来进行合并,比如说我们想让item可以上下拖拽,左右滑动移除,则可以设置为 

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
        int dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlag = ItemTouchHelper.START | ItemTouchHelper.END;
        return makeMovementFlags(dragFlag, swipeFlag);
    }

makeMovementFlags(int dragFlags, int swipeFlags) 方法中两个形参的名字,我们也可以看出来,一个是拖拽的flag,一个是横滑的flag。

同时,为了在拖动时有回调以便于我们做自己的逻辑,所以定义个回调接口

public interface OnItemTouchListener {

    boolean onItemMove(int fromPosition, int toPosition);

    void onSwipeDismiss(int position);

}

SimpleItemTouchHelperCallback 中重写拖拽和横滑的方法 

  private OnItemTouchListener adapter;

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {

        if (viewHolder.getItemViewType() != target.getItemViewType())
            return false;
        adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

        adapter.onSwipeDismiss(viewHolder.getAdapterPosition());
    }

同时 SwipeDragAdapter 实现 OnItemTouchListener 接口,写入自己的逻辑 

   @Override
    public boolean onItemMove(int fromPosition, int toPosition) {
        Collections.swap(mList, fromPosition, toPosition);
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

    @Override
    public void onSwipeDismiss(int position) {
        mList.remove(position);
        notifyItemRemoved(position);
    }

在 Activity 中,把 Adapter 和 ItemTouchHelper 及 RecyclerView 关联起来,

    ItemTouchHelper.Callback itemTouchHelperCallback = new SimpleItemTouchHelperCallback(swipeDragAdapter);
    ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchHelperCallback);
    itemTouchHelper.attachToRecyclerView(recyclerView);

这样,就可以实现我们想要的功能了。

此时,如果我们按下item,左右滑动时,item会随着手指移动,加入自己的逻辑,划出后会把该条目删除掉;长按item,上下滑动时,item会被拖拽的离开原先的位置,我们需要在对应的回调中调整集合中两个元素的位置,同时刷新界面;这里注意一点,必须是长按item才可以拖拽,如果我们想在item上添加一个图标,按下图标就能拖拽item,这个功能可以实现吗?如果了解 ItemTouchHelper 源码,就会发现,其实拖拽的起始于一个方法 startDrag() ,这个方法注释里也有用法说明

    /*
     *     viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() {
     *         public boolean onTouch(View v, MotionEvent event) {
     *             if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) {
     *                 mItemTouchHelper.startDrag(viewHolder);
     *             }
     *             return false;
     *         }
     *     });
     */

如果我们不想在适配器中调用,可以再定义一个接口做回调处理;同理,左右滑动事件也有相应的方法 startSwipe(),用法相同。如果我们想在item拖动时改变写item的颜色背景,停止拖动时恢复原状,我们也可以定义回调来实现逻辑,下面给一个完整点的代码。


public interface ItemTouchViewHolder {
    void onItemSelected();

    void onItemClear();
}

public interface StartDragListener {
    void onStartDrag(RecyclerView.ViewHolder holder);
}

public class SwipeDragAdapter extends RecyclerView.Adapter<SwipeDragAdapter.SwipePressViewHolder>
        implements OnItemTouchListener {

    private Context mContext;
    private ArrayList<String> mList;
    private StartDragListener mDragListener;

    public SwipeDragAdapter(Context context, ArrayList<String> list) {

        this.mContext = context;
        this.mList = list;
    }


    public void setDragListener(StartDragListener dragListener) {
        this.mDragListener = dragListener;
    }

    @Override
    public SwipePressViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {

        View view = LayoutInflater.from(viewGroup.getContext())
                .inflate(R.layout.item_swipe_press_rv, viewGroup, false);
        return new SwipePressViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final SwipePressViewHolder holder, int position) {

        String url = mList.get(position);
        holder.tvContent.setText(position + "        " + url);

        //拖动图标设置OnTouch接口使其具有和长按item拖动一样的效果
        holder.ivDrag.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {

                if (mDragListener != null &&
                        MotionEventCompat.getActionMasked(motionEvent) == MotionEvent.ACTION_DOWN)
                    mDragListener.onStartDrag(holder);
                return false;
            }
        });

    }

    @Override
    public int getItemCount() {
        int count = 0;
        if (mList != null && !mList.isEmpty()) {
            count = mList.size();
        }
        return count;
    }

    @Override
    public boolean onItemMove(int fromPosition, int toPosition) {

        Collections.swap(mList, fromPosition, toPosition);
        notifyItemMoved(fromPosition, toPosition);
        return true;
    }

    @Override
    public void onSwipeDismiss(int position) {

        mList.remove(position);
        notifyItemRemoved(position);
    }

    class SwipePressViewHolder extends RecyclerView.ViewHolder implements ItemTouchViewHolder {

        private TextView tvContent;
        private ImageView ivDrag;

        SwipePressViewHolder(View itemView) {
            super(itemView);

            tvContent = (TextView) itemView.findViewById(R.id.tv_swipe_press);
            ivDrag = (ImageView) itemView.findViewById(R.id.iv_drag_swipe_press);
        }

        @Override
        public void onItemSelected() {
            tvContent.setTextColor(ContextCompat.getColor(mContext, R.color.colorAccent));
        }

        @Override
        public void onItemClear() {
            tvContent.setTextColor(ContextCompat.getColor(mContext, R.color.color_3));
        }
    }

}


public class SimpleItemTouchHelperCallback extends ItemTouchHelper.Callback {

    private final static String TAG = "TouchHelperCallback";
    private final float ALPHA = 1.0f;
    private OnItemTouchListener adapter;
    private boolean isLongPressEnable = true;
    private boolean isSwipeEnable = true;

    public SimpleItemTouchHelperCallback(OnItemTouchListener adapter) {

        this.adapter = adapter;
    }

    @Override
    public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        int dragFlag = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
        int swipeFlag = ItemTouchHelper.START | ItemTouchHelper.END;
        return makeMovementFlags(dragFlag, swipeFlag);
    }

    @Override
    public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {

        if (viewHolder.getItemViewType() != target.getItemViewType())
            return false;
        adapter.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
        return true;
    }

    @Override
    public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {

        adapter.onSwipeDismiss(viewHolder.getAdapterPosition());
    }


    @Override
    public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) {

        if (actionState == ItemTouchHelper.ACTION_STATE_DRAG
                && viewHolder instanceof ItemTouchViewHolder) {

            ((ItemTouchViewHolder) viewHolder).onItemSelected();
        }

        super.onSelectedChanged(viewHolder, actionState);

    }

    @Override
    public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {

        if (viewHolder instanceof ItemTouchViewHolder) {

            ((ItemTouchViewHolder) viewHolder).onItemClear();
        }
        super.clearView(recyclerView, viewHolder);
    }

    @Override
    public boolean isLongPressDragEnabled() {
        return isLongPressEnable;
    }

    @Override
    public boolean isItemViewSwipeEnabled() {
        return isSwipeEnable;
    }

}

Activity 中调用

    SwipeDragAdapter swipeDragAdapter = new SwipeDragAdapter(this, list);
    recyclerView.setAdapter(swipeDragAdapter);
    ItemTouchHelper.Callback itemTouchHelperCallback = new SimpleItemTouchHelperCallback(swipeDragAdapter);
    final ItemTouchHelper itemTouchHelper = new ItemTouchHelper(itemTouchHelperCallback);
    itemTouchHelper.attachToRecyclerView(recyclerView);

    swipeDragAdapter.setDragListener(new StartDragListener() {
        @Override
        public void onStartDrag(RecyclerView.ViewHolder holder) {

            itemTouchHelper.startDrag(holder);
        }
    });
发布了176 篇原创文章 · 获赞 11 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/Deaht_Huimie/article/details/101033206