ItemTouchHelper拖拽过程中ActionStateDrag回调不触发问题
问题根因
ItemTouchHelper.Callback的onSelectedChanged回调本身是状态变更触发型回调,只有当ViewHolder的交互状态发生切换时才会执行:
- 从空闲状态切到拖拽状态时,才会派发一次
ACTION_STATE_DRAG事件 - 拖拽持续过程中只要状态没有发生切换,这个回调不会重复触发
- 拖拽结束切回空闲状态时,会派发
ACTION_STATE_IDLE事件
你把分组指示器的实时更新逻辑写在ACTION_STATE_DRAG分支里,自然只会在拖拽启动时执行一次,拖拽过程中命不中断点是符合组件逻辑的正常表现。
适配方案
要监听拖拽全程的位置更新,使用ItemTouchHelper.Callback提供的onChildDraw/onChildDrawOver回调即可,这两个回调会在拖拽item每一次位置重绘时触发,完整覆盖拖拽启动、移动、松手前的全流程。
具体实现逻辑如下:
- 保留现有
onSelectedChanged中ACTION_STATE_DRAG分支的代码,仅负责指示器初始化:比如首次显示分组指示器、做初始位置校准 - 重写
onChildDraw(或onChildDrawOver)方法,在这里实现指示器的实时更新:
override fun onChildDrawOver( canvas: Canvas, recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, dX: Float, dY: Float, actionState: Int, isCurrentlyActive: Boolean ) { super.onChildDrawOver(canvas, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) // 仅在拖拽状态下执行指示器逻辑 if (actionState != ItemTouchHelper.ACTION_STATE_DRAG) return // 拿到拖拽item的实时垂直中心坐标 val itemView = viewHolder.itemView val currentCenterY = itemView.top + dY + itemView.height / 2 // 遍历当前可见的分组项,判断拖拽位置所属分组,更新指示器位置、高亮边框、分组提示文案 refreshGroupIndicatorWhenDrag(currentCenterY, recyclerView) }
- 在
onSelectedChanged的ACTION_STATE_IDLE分支中,处理拖拽结束逻辑:隐藏分组指示器、提交最终的分组拖拽结果、刷新列表
注意:两个绘制回调的区别是执行时机不同:
onChildDraw在RecyclerView绘制item内容之前执行,绘制的指示器会被item内容覆盖onChildDrawOver在item内容绘制完成后执行,绘制的指示器会浮在item上层,适合做悬浮提示、分组高亮边框这类效果
不要把指示器更新逻辑写在onMove回调里:onMove仅在拖拽item和其他列表项触发位置交换时才会回调,如果拖拽过程中item停在两个条目中间、没有触发换位,onMove不会执行,会导致指示器更新卡顿、不跟手。
内容的提问来源于stack exchange,提问作者andry fritzent
相关产品推荐
相关产品推荐

