RecycleView拖动排序

619 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第13天,点击查看活动详情

需求

某一天,一个同事向我吐槽:艾玛你这App啥玩意,太难用了!

我:???

同事:就比如说你这个列表,固定按创建顺序来排序。

我:这不是很正常吗?有啥问题?

同事:那要是我创建顺序搞错了,就得重新删掉再按顺序重新创建一遍,太不方便了!

我:这是你操作问题,咋还赖上我的App咧?

同事:我不管!就是难用!

虽然最后也没真的去修改,但作为未雨绸缪的Coder,我不禁思考起来,排序能不能优化呢?首先想到的是弄个按钮做切换不同的排序策略,比如创建时间升降序、文字升降序等。但还是觉得不足够灵活。最后还是觉得让用户自定义排序,你爱咋排咋排。

RecycleView拖动排序

官方其实早已明了我们的需求了,推出了ItemTouchHelper帮助类来让开发者更容易地完成拖动操作(drag)以及滑动操作(swipe)。

实现拖动排序跟把大象放进冰箱一样,拢共三步:

  1. 继承ItemTouchHelper.Callback
  2. 使用Callback的实现构造ItemTouchHelper实例。
  3. 将ItemTouchHelper实例与RecycleView绑定。

实现ItemTouchHelper.Callback

对于拖动排序这种简单的操作,相比于直接继承Callback,更推荐使用预置的Callback抽象子类SimpleCallback。

首先我们来看看SimpleCallback的构造方法:

public SimpleCallback(int dragDirs, int swipeDirs) {
    mDefaultSwipeDirs = swipeDirs;
    mDefaultDragDirs = dragDirs;
}
  • dragDirs:拖动方向。假如垂直布局的拖动方向一般就是上下;水平布局就是左右;流布局则通常为四个方向都可以。
  • swipeDirs:滑动方向。常见的场景是垂直布局,把Item向左或者向右滑删除Item,这里用到的就是swipeDirs。一般与拖动方向垂直。

dragDirs与swipeDirs都取值与ItemTouchHelper的常量值:

ValueDescription
ItemTouchHelper.UP向上方向
ItemTouchHelper.DOWN向下方向
ItemTouchHelper.START向前方向(从左到右布局即向左)
ItemTouchHelper.END向后方向(从左到右布局即向右)

以上的值可以通过与、或运算混合使用。

再来看看需要实现的两个方法:

public abstract boolean onMove(
    //触发事件的RV
    @NonNull RecyclerView recyclerView,
    //正在拖动的Item对应的VH
    @NonNull ViewHolder viewHolder, 
    //拖动到的Item对应的VH
    @NonNull ViewHolder target
);
​
public abstract void onSwiped(@NonNull ViewHolder viewHolder, int direction);
  • onMove。当拖动发生并移动到其他Item位置上时调用。也就是说如果按着Item,移动经过了10个Item,就会被调用10次。
  • onSwipe。滑动发生时调用。

实现拖动排序重点是onMove,整个Callback如下:

val myCallback = object : ItemTouchHelper.SimpleCallback(
    ItemTouchHelper.UP or ItemTouchHelper.DOWN,
    0
) {
    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean {
        //取出原位置的position
        val from = viewHolder.adapterPosition
        //取出目标位置的position
        val to = target.adapterPosition
        //取出原位置的数据
        val e = list[from]
        //删除原位置的数据
        list.removeAt(from)
        //向目标位置添加原位置的数据
        list.add(to, e)
        //通知Adapter移动了Item
        recyclerView.adapter?.notifyItemMoved(from, to)
        return true
    }
​
    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) = Unit
}

构造ItemTouchHelper实例

这就很简单了,没啥好说的了。

val helper = ItemTouchHelper(myCallback)

关联RecycleView

同样很简单!

helper.attachToRecyclerView(rv)

结言

通过对ItemTouchHelper的学习,掌握到了RecycleView拖动以及滑动相关的实现,实在是受益匪浅。