创建recycleView,将recycleView的item实现拖拽。
新建DefaultItemTouchHelper类,继承自ItemTouchHelper,DefaultItemTouchHelper不需要实现什么方法,它的任务就是将工作代理给Callback,然后将Callback作为参数传入。
新建DefaultItemTouchHelpCallback类。继承自ItemTouchHelper.callback()方法,这个类至少实现getMovementFlags()、onMove()、onSwiped()方法。
其中一些主要方法的作用是 :
getMovementFlags: 主要在这个方法中规定可进行拖拽的方向,可进行滑动的方向。
onMove: 主要是如果实现拖拽,进行拖拽的item,可以在这进行子view的位置的替换
onSwiped: 主要是如果实现滑动,可以在这里进行子View的位置的删除操作。
isItemViewSwipeEnabled() : 返回值是是否支持滑动
isLongPressDragEnabled : 返回值是是否支持拖拽
onSelectedChanged : 在这里更新UI,当被拖动的或者是被滑动的viewHolder改变时调用,actionState会返回当前ViewHolder的状态,有三个状态值。
* action_state_swipe : 当View刚被滑动时返回
* action_state_drag : 当View刚被拖动时返回
* action_state_idle : 当View即没被拖动也没有被滑动时或者是滑动状态还没被触发时,返回这个状态
* 在这里就可以进行拖动时候进行高亮显示,
clearView: 刚刚说在onSelectedChanged中可以进行在view改变位置时更新UI,比如说进行高亮显示等,那么在clearView中是进行UI的复原,比如进行高亮显示的取消。
主要的方法就是以上几个。
但是,如果说我们直接在DefaultItemTouchHelpCallback类中将所有的方法都规定实现,本来是定义一个callback就可以了,传给ItemTouchHelper生成实例,ItemTouchHelper的作用就是RecycleView进项绑定,其余的都在callback进行处理,但是如果只是定义一个callback的话自己进行实现自己需要的拖拽,那么只能实现这一种情况,就比如我这里要实现的是左右拖拽,这样这个callback就是一个定制callback,如果遇到其他也需要进行拖拽处理的情况需要重新建个类去实现。
所以这里是定义一个接口,新建个ItemTouchDelegate接口,分别空实现onMove(),onSwiped(),uiOnSwiping(),uiOnDragging(),uiOnClearView(),其中getMovementFlags()我们默认实现,让ItemTouchHelper进支持上下方向的拖动、其他行为禁止,也即能满足我们的需求。
所以创建ItemTouchDeleGate接口
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper.*
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
/**
本来是定义一个callback就可以了,传给ItemTouchHelper生成实例,ItemTouchHelper的作用就是与RecycleView进项绑定
其余的都在callback进行处理,
但是如果只是定义一个callback的话自己进行实现自己需要的拖拽,那么只能实现这一种情况,就比如我这里要实现的是左右拖拽,这样这个callback就是一个定制callback
如果遇到其他也需要进行拖拽处理的情况需要重新建个类去实现。
所以这里是定义一个接口,新建个ItemTouchDelegate接口,
分别空实现onMove(),onSwiped(),uiOnSwiping(),uiOnDragging(),uiOnClearView(),
其中getMovementFlags()我们默认实现,让ItemTouchHelper进支持上下方向的拖动、其他行为禁止,也即能满足我们的需求。
*/
interface ItemTouchDelegate {
//getMovementFlags中定义是否处理拖拽事件和滑动事件,以及定义拖拽的方向和滑动的方向
//ragFlags 是拖拽标志,swipeFlags是滑动标志,我们把swipeFlags 都设置为0,表示不处理滑动操作。
fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Array<Int> {
val layoutManager = recyclerView.layoutManager
var swipeFlag = 0
var dragFlag = 0
if (layoutManager is LinearLayoutManager) {
if (layoutManager.orientation == LinearLayoutManager.VERTICAL) {
swipeFlag = 0 // 不允许滑动
dragFlag = (UP or DOWN) // 允许上下拖拽
} else {
swipeFlag = 0
dragFlag = (LEFT or RIGHT) // 允许左右拖拽
}
}else if(layoutManager is GridLayoutManager) {
dragFlag = (UP or DOWN or LEFT or RIGHT) //允许上下左右进行拖拽
swipeFlag = 0
}
return arrayOf(dragFlag, swipeFlag)
}
//当我们设置了非0的dragFlag时,可以进行item的拖拽,在拖拽的过程中不断地回调onMove()方法
//因为拖拽的过程中两个item的位置进行了改变,我们应该知道两个item的所处位置的ViewHolder,
//然后让两个ViewHolder进行数据集的交换并调用Adapter的notifyItemMoved来刷新item
fun onMove(srcPosition: Int, targetPosition:Int): Boolean = true
//当用户正在滑动子View时调用,可以在这里进行子View的删除操作。
fun onSwiped(position: Int, direction: Int) {
}
// 刚开始滑动时,需要进行的UI操作
fun uiOnSwiping(viewHolder: RecyclerView.ViewHolder?) {
}
// 刚开始拖动时,需要进行的UI操作
fun uiOnDragging(viewHolder: RecyclerView.ViewHolder?) {
}
// 用户释放与当前itemView的交互时,可在此方法进行UI的复原
fun uiOnClearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
}
}
在DefaultItemTouchHelpCallback 中进行实现
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_DRAG
import androidx.recyclerview.widget.ItemTouchHelper.ACTION_STATE_SWIPE
import androidx.recyclerview.widget.RecyclerView
import org.jetbrains.annotations.NotNull
import javax.inject.Inject
class DefaultItemTouchHelpCallback(@NotNull val helperDelegate : ItemTouchDelegate) :
ItemTouchHelper.Callback () {
//dragFlags确定拖动方向,swipeFlags确定滑动删除
//一个是用户长按后的拖拽状态,一个是手指左右滑动的滑动状态
private var canDrag: Boolean? = null
private var canSwipe: Boolean? = null
fun setDragEnable(enable: Boolean) {
canDrag = enable
}
fun setSwipeEnable(enable: Boolean) {
canSwipe = enable
}
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
val flags = helperDelegate.getMovementFlags(recyclerView, viewHolder)
return if (flags != null && flags.size >= 2) {
makeMovementFlags(flags[0], flags[1])
} else makeMovementFlags(0, 0)
}
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
return helperDelegate.onMove(viewHolder.adapterPosition, target.adapterPosition)
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
helperDelegate.onSwiped(viewHolder.adapterPosition, direction)
}
override fun isItemViewSwipeEnabled(): Boolean {
return canSwipe == true
}
override fun isLongPressDragEnabled(): Boolean {
return canDrag == true
}
/**
* 更新UI
* 当被拖动或者是被滑动的ViewHolder改变时调用,actionState会返回当前viewHolder的状态,有三个值
* action_state_swipe : 当View刚被滑动时返回
* action_state_drag : 当View刚被拖动时返回
* Action_state_idle : 当View即没被拖动也没有被滑动时或者是滑动状态还没被触发时,返回这个状态
* 在这里就可以进行拖动时候进行高亮显示,
*/
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
super.onSelectedChanged(viewHolder, actionState)
when(actionState) {
ACTION_STATE_SWIPE -> {
helperDelegate.uiOnSwiping(viewHolder)
}
ACTION_STATE_DRAG -> {
helperDelegate.uiOnDragging(viewHolder)
}
}
}
/**
* 更新UI
* 当View被拖动或者是滑动完后并且已经结束了运动动画时调用,进行uI的复原,例如当View固定位置后,让View的显示取消高亮
*/
override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
super.clearView(recyclerView, viewHolder)
helperDelegate.uiOnClearView(recyclerView, viewHolder)
}
}
DefaultItemTouchHelper中就是将callback实例传入,所以将创建好的DefaultItemTouchHelpCallback传入,
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
class DefaultItemTouchHelper(private val callback : DefaultItemTouchHelpCallback): ItemTouchHelper(callback) {
fun setDragEnable(enable: Boolean) {
callback.setDragEnable(enable)
}
fun setSwipeEnable(enable: Boolean) {
callback.setSwipeEnable(enable)
}
override fun startDrag(viewHolder: RecyclerView.ViewHolder) {
super.startDrag(viewHolder)
}
}
使用时就直接在recycleView初始化后进行对callback的初始化,将需要实现的方法进行实现,然后将callback实例传入TouchHelper中,这样recycleView的子view就可以进行拖拽了。
最主要要实现的方法就是onMove、onSwiped、onSelectedChanged、clearView方法。
var itemTouchHelpCallback = DefaultItemTouchHelpCallback(object : ItemTouchDelegate{
override fun onMove(srcPosition: Int, targetPosition: Int): Boolean {
if(mViewModel.getTryList().size > 1 && srcPosition < mViewModel.getTryList().size && targetPosition < mViewModel.getTryList().size){
Collections.swap(mViewModel.getTryList(), srcPosition, targetPosition)
tryAdapter.notifyItemMoved(srcPosition, targetPosition)
return true
}
return false
}
// override fun uiOnDragging(viewHolder: RecyclerView.ViewHolder?) {
// viewHolder?.itemView?.setBackgroundColor(Color.parseColor("@drawable/gray_round_bg_16"))
// }
//
// override fun uiOnClearView(
// recyclerView: RecyclerView,
// viewHolder: RecyclerView.ViewHolder
// ) {
// viewHolder.itemView.setBackgroundColor(Color.parseColor("@drawable/white_round_bg_16"))
// }
})
itemTouchHelper = DefaultItemTouchHelper(itemTouchHelpCallback)
itemTouchHelper.attachToRecyclerView(mBinding.rcyTryTool)
itemTouchHelper.setDragEnable(true)
itemTouchHelper.setSwipeEnable(false)