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

嵌套RecyclerView二次拖动子项失效问题排查求助

嵌套RecyclerView拖动功能失效问题排查与修复

问题描述

父RecyclerView的主项支持展开/收起,展开后内部子RecyclerView实现拖动排序功能。初次打开主项时子项拖动正常,但关闭并重新打开同一主项后,子项无法跟随光标拖动。

代码错误分析

  1. 重复创建Adapter与ItemTouchHelper:在bind方法的展开分支中,每次主项展开都会重新创建ExpandedItemAdapter和ItemTouchHelper实例并重新attach到子RecyclerView。由于ViewHolder复用机制,关闭时设置adapter为null会破坏原有绑定,再次打开时新创建的ItemTouchHelper无法正确拦截触摸事件。
  2. 全局刷新打乱绑定状态:点击展开/收起时调用notifyDataSetChanged()触发全局ViewHolder刷新,导致已绑定的ItemTouchHelper状态失效。
  3. ItemTouchHelper未复用:ItemTouchHelper与子RecyclerView的绑定未与ViewHolder生命周期关联,每次展开都重复创建,引发绑定冲突。

解决方案

1. 复用ExpandedItemAdapter与ItemTouchHelper

将子适配器实例存储到ItemModel中避免重复创建;同时用tag标记子RecyclerView是否已绑定ItemTouchHelper,仅在首次绑定:

// 修改ItemModel,添加适配器缓存字段
data class ItemModel(
    // 原有业务字段...
    val expandedList: MutableList<YourExpandedItemType>,
    var expandedAdapter: ExpandedItemAdapter? = null
)

// 修改ViewHolder的bind方法
fun bind(item: ItemModel) {
    binding.viewModel = item
    val isExpanded = adapterPosition == expandedItemPosition
    binding.isExpanded = isExpanded
    binding.executePendingBindings()

    val expandedRecyclerView = binding.expandedRecyclerView
    if (isExpanded) {
        // 复用已有适配器,无则创建并缓存
        val adapter = item.expandedAdapter ?: ExpandedItemAdapter(item.expandedList).also {
            item.expandedAdapter = it
        }
        expandedRecyclerView.adapter = adapter

        // 仅首次绑定ItemTouchHelper
        if (expandedRecyclerView.tag == null) {
            val itemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(
                ItemTouchHelper.UP or ItemTouchHelper.DOWN,
                0
            ) {
                override fun onMove(
                    recyclerView: RecyclerView,
                    viewHolder: RecyclerView.ViewHolder,
                    target: RecyclerView.ViewHolder
                ): Boolean {
                    val from = viewHolder.adapterPosition
                    val to = target.adapterPosition
                    adapter.onItemMove(from, to)
                    return true
                }

                override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {}

                override fun isLongPressDragEnabled(): Boolean = true
            })
            itemTouchHelper.attachToRecyclerView(expandedRecyclerView)
            expandedRecyclerView.tag = true // 标记已完成绑定
        }
    } else {
        expandedRecyclerView.adapter = null
    }
}

2. 替换全局刷新为局部刷新

点击展开/收起时,仅刷新状态变化的两个主项,避免全局ViewHolder重建:

init {
    binding.root.setOnClickListener {
        val currentPos = adapterPosition
        val prevExpandedPos = expandedItemPosition
        
        expandedItemPosition = if (currentPos == prevExpandedPos) -1 else currentPos
        
        // 局部刷新受影响的Item
        if (prevExpandedPos != -1) notifyItemChanged(prevExpandedPos)
        if (expandedItemPosition != -1) notifyItemChanged(expandedItemPosition)
    }
}

3. 验证绑定逻辑

确保子RecyclerView在ViewHolder复用过程中,ItemTouchHelper仅绑定一次,后续复用直接使用已绑定的实例,避免重复attach导致的事件拦截失效。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 09:04:53