You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.26 18:48:24