RecyclerView 滑动删除与拖动的实现

前言

记得很久以前做APP应用的时候,项目组老大说网易的栏目管理那块,可拖动排序蛮好看的,我们的应用也要那么做,后来我就在网上百度一番,找到用GridView的实现,最近游览网页,在网上看见有用RecyclerView实现的,自己也按照文章上的写了,看了一下,觉得写的蛮好的,不过有些地方的注释写的不全,所以我也写了一篇记录下来,参考文地址在这里,感谢这位作者,使我又学会了很多。

实现原理

主要是借助 ItemTouchHelper.Callback 类来实现,我们要关注的方法为
* getMovementFlags( )
* onMove()
* onSwiped()
* onSelectedChanged()
* clearView()
* isLongPressDragEnabled()
只需要重写这些方法,就能得到我们想要的结果。

拖动实现

首先自定义一个MyCallback类继承 ItemTouchHelper.Callback ,定义两个int变量dragFlagsswipeFlags 并实现下方法。

getMovementFlags()

这个方法主要是为了获取我们当前的事件是拖动还是滑动

dragFlags = 0;
        swipeFlags = 0;
        if (recyclerView.getLayoutManager() instanceof GridLayoutManager || recyclerView.getLayoutManager() instanceof StaggeredGridLayoutManager) {
                dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
            } else {
                dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
            }
        return makeMovementFlags(dragFlags, swipeFlags);

首先在这里为我们的标志符赋值为0,然后通过 ItemTouchHelper 来为标识符赋值。

onMove()

在该方法的注释中有这一段说明

viewHolder The ViewHolder which is being dragged by the user.
target The ViewHolder over which the currently active item is being dragged.

这两个都是onMove提供的参数,从注释中可以看出viewHolder是起始位置,而target是目标位置,那么我们就好办了

int fromPosition = viewHolder.getAdapterPosition();
            int toPosition = target.getAdapterPosition();
            //这里的toPosition和fromPosition结合可以设置那一项不能改变
            if (toPosition >= 2 && fromPosition != 0 && fromPosition !=1){
                if (fromPosition < toPosition) {
                    //向下拖动
                    for (int i = fromPosition; i < toPosition; i++) {
                        Collections.swap(mListData, i, i + 1);
                    }
                }else {
                    //向上拖动
                    for (int i = fromPosition; i > toPosition; i--) {
                        Collections.swap(mListData, i, i - 1);
                    }
                }
                recyclerView.getAdapter().notifyItemMoved(fromPosition,toPosition);
            }
            return true;

这里我设置了前两项不可交换位置,往往我们都会有这样的需求。通过Collections 来交换数据,最后通过ecyclerView.getAdapter().notifyItemMoved 来刷新页面。

与RecyclerView绑定

通过ItemTouchHelper的attachToRecyclerView方法与RecyclerView绑定在一起

ItemTouchHelper helper =  new ItemTouchHelper(new MyItemTouchHelperCallback());
        helper.attachToRecyclerView(mRecyclerView);

其中的MyItemTouchHelperCallback就是我们的自定义继承了 ItemTouchHelper.Callback 类的实现。这样并没有长按出现的效果,用户体验不好,可在下面的方法中实现。

onSelectedChanged

我们可以在这个方法中自定义用户长按后出现的效果,我这里就只设置了放大。

if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
                viewHolder.itemView.setScaleX(1.05f);
                viewHolder.itemView.setScaleY(1.05f);
            }

如果只设置了方法,而不设置缩小。那么肯定是不行的,我们在下面的方法中去还原View。

clearView

当事件结束后,我们可以在这里做一些处理,比如上面的还原View。

viewHolder.itemView.setScaleX(1.0f);
viewHolder.itemView.setScaleY(1.0f);

到这里我们的拖动就结束了,下面我们再来实现滑动删除。

滑动删除

滑动删除和拖动也是一样的实现思路,也是通过getMovementFlags获取事件,我们在前面的代码中添加如下代码

swipeFlags = ItemTouchHelper.START | ItemTouchHelper.END;

这样就可以了,如果你只想GridLayoutManager或LinearLayoutManager有滑动删除,那么可以把代码添加到上面的if判断里面去。

onSwiped

删除的实现,这是一个抽象方法,我们必须实现的。

int position = viewHolder.getAdapterPosition();
mRecyclerView.getAdapter().notifyItemRemoved(position);
mListData.remove(position);

这个方法比较简单,就是删除数据,更新页面。
如果你不想你的某一些项不可以滑动删除,那么就可以在前面的getMovementFlags去做判断,只要不为swipeFlags赋值就可以了。

isLongPressDragEnabled

该方法默认所有的项都可以拖动,在这里直接返回false,然后在为
RecyclerView添加的事件监听中为长按事件添加如下代码就可以了

helper.startDrag(vh);

这样该Item项就可以拖动了,其中的helper为上面的ItemTouchHelper,vh就不用解释了吧。

如果对于RecyclerView的Item点击事件还不了解的可以阅读RecyclerView item点击你真的会么

总结

RecyclerView是如此的强大,对于滑动与拖动,只需要弄明白上面的6个方法的调用顺序,各个方法的任务。其中最重要的就是 getMovementFlags 了,如果这里弄错了,那么后面的都没用。