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

Android Kotlin:跨RecyclerView拖拽至空白/空列表失效问题求助

跨RecyclerView拖拽:解决空白区域/空列表无法接收拖拽项的问题

问题描述

  • 基于ItemTouchHelper实现跨RecyclerView拖拽功能,参考旧Java项目改写为Kotlin
  • 当前仅能将Item拖至目标RecyclerView的已有Item上触发ACTION_DROP,拖至空白区域或空RecyclerView时无响应,无法实现添加到列表末尾的效果
  • 尝试监听ACTION_DRAG_EXITED但事件触发异常,未找到有效解决方案

核心问题分析

目标RecyclerView的空白区域默认不会接收拖拽事件,只有当拖拽项覆盖到子View(列表Item)时才会触发ACTION_DROP。空RecyclerView没有子View,自然无法响应拖拽释放操作。

解决方案

方案1:给RecyclerView添加全屏拖拽接收容器

在RecyclerView布局中添加一个与RecyclerView同大小的透明View,让它接收空白区域的拖拽事件:

  1. 修改布局文件,用FrameLayout包裹RecyclerView并添加透明View:
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvTop"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <View
        android:id="@+id/drag_receiver_top"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/transparent"
        android:clickable="true"/>
</FrameLayout>
  1. 修改DragListener逻辑,处理透明View的拖拽事件:
class DragListener(private val listener: Listener) : View.OnDragListener {

private var isDropped = false

override fun onDrag(v: View, event: DragEvent): Boolean {
    when (event.action) {
        DragEvent.ACTION_DROP -> {
            Log.d("MyLog", "ACTION_DROP")
            isDropped = true
            var positionTarget: Int

            val viewSource = event.localState as? View
            val viewId = v.id
            val flItem = R.id.frame_layout_item
            val tvEmptyListTop = R.id.tvEmptyListTop
            val tvEmptyListBottom = R.id.tvEmptyListBottom
            val rvTop = R.id.rvTop
            val rvBottom = R.id.rvBottom
            val dragReceiverTop = R.id.drag_receiver_top
            val dragReceiverBottom = R.id.drag_receiver_bottom
            
            when (viewId) {
                flItem, tvEmptyListTop, tvEmptyListBottom, rvTop, rvBottom, dragReceiverTop, dragReceiverBottom -> {
                    val target: RecyclerView = when (viewId) {
                        tvEmptyListTop, rvTop, dragReceiverTop -> v.rootView.findViewById(rvTop)
                        tvEmptyListBottom, rvBottom, dragReceiverBottom -> v.rootView.findViewById(rvBottom)
                        else -> v.parent as RecyclerView
                    }

                    // 拖拽到空白容器时,设置为末尾位置
                    positionTarget = if (viewId == dragReceiverTop || viewId == dragReceiverBottom) {
                        -1
                    } else {
                        v.tag as? Int ?: -1
                    }

                    if (viewSource != null) {
                        val source = viewSource.parent as RecyclerView
                        val adapterSource = source.adapter as ListAdapter
                        val positionSource = viewSource.tag as Int
                        val list = adapterSource.getList()[positionSource]
                        val listSource = adapterSource.getList().toMutableList()

                        listSource.removeAt(positionSource)
                        adapterSource.updateList(listSource)
                        adapterSource.notifyDataSetChanged()

                        val adapterTarget = target.adapter as ListAdapter
                        val customListTarget = adapterTarget.getList().toMutableList()
                        if (positionTarget < 0) {
                            customListTarget.add(list)
                        } else {
                            customListTarget.add(positionTarget, list)
                        }
                        adapterTarget.updateList(customListTarget)
                        adapterTarget.notifyDataSetChanged()
                    }
                }
            }
        }
    }

    if (!isDropped && event.localState != null) {
        (event.localState as? View)?.visibility = View.VISIBLE
    }
    return true
}
}

方案2:直接利用RecyclerView本身处理拖拽事件

无需额外添加View,直接给RecyclerView设置OnDragListener,并标记空白区域的拖拽为末尾位置:
修改DragListener中的位置判断逻辑:

// 在when(viewId)分支内
positionTarget = if (viewId == rvTop || viewId == rvBottom) {
    // 拖拽到RecyclerView空白区域,添加到末尾
    -1
} else {
    v.tag as Int
}

注意:需给RecyclerView设置android:clickable="true"或android:focusable="true",否则无法接收拖拽事件。

关键优化点

  • 确保拖拽接收区域具备事件接收能力(设置clickable/focusable为true)
  • 区分已有Item和空白区域的拖拽逻辑,空白区域直接将Item添加到列表末尾
  • 无需依赖ACTION_DRAG_EXITED事件,避免其异常触发问题

内容的提问来源于stack exchange,提问作者Dmitrii

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:25:28