嵌套RecyclerView二次拖动子项失效问题排查求助
嵌套RecyclerView拖动功能失效问题排查与修复
问题描述
父RecyclerView的主项支持展开/收起,展开后内部子RecyclerView实现拖动排序功能。初次打开主项时子项拖动正常,但关闭并重新打开同一主项后,子项无法跟随光标拖动。
代码错误分析
- 重复创建Adapter与ItemTouchHelper:在
bind方法的展开分支中,每次主项展开都会重新创建ExpandedItemAdapter和ItemTouchHelper实例并重新attach到子RecyclerView。由于ViewHolder复用机制,关闭时设置adapter为null会破坏原有绑定,再次打开时新创建的ItemTouchHelper无法正确拦截触摸事件。 - 全局刷新打乱绑定状态:点击展开/收起时调用
notifyDataSetChanged()触发全局ViewHolder刷新,导致已绑定的ItemTouchHelper状态失效。 - 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
相关产品推荐
相关产品推荐

