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

ItemTouchHelper长按未拖拽后RecyclerView项未正确移除问题求助

RecyclerView拖拽后长按不移动导致元素异常的修复方案

问题现象

原本点击RecyclerView元素即可移除,但绑定ItemTouchHelper实现拖拽排序后,长按某一项但不拖拽到新位置时,该项会从列表数据中被移除(getItemCount返回0),但视图仍保持可见且固定,新增元素会直接覆盖它。

问题演示

问题代码

MainActivity

class MainActivity : AppCompatActivity() {
    @SuppressLint("MissingInflatedId")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val recyclerView = findViewById<RecyclerView>(R.id.recView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        val myAdapter = MyAdapter(layoutInflater,mutableListOf("A","B","C","D","E","F")){items,position->
            items.removeAt(position)
            recyclerView.adapter?.notifyItemRemoved(position)
        }
        recyclerView.adapter =  myAdapter
        val myItemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN,0){
            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: ViewHolder,
                target: ViewHolder
            ): Boolean {
                val item = myAdapter.items[viewHolder.adapterPosition]
                myAdapter.items.removeAt(viewHolder.adapterPosition)
                myAdapter.items.add(target.adapterPosition,item)
                recyclerView.adapter?.notifyItemMoved(viewHolder.adapterPosition,target.adapterPosition)
                return true
            }

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

        })
        myItemTouchHelper.attachToRecyclerView(recyclerView)
    }
}

MyAdapter

class MyAdapter(val layoutInflater: LayoutInflater, val items: MutableList<String>,val onItemClicked: (items: MutableList<String>,position: Int)->Unit): RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    class MyViewHolder(view: View): ViewHolder(view){
        val textView = view.findViewById<TextView>(R.id.textview)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(layoutInflater.inflate(R.layout.list_item,parent,false))
    }

    override fun getItemCount(): Int {
        return items.size
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.textView.text = items[position]
        holder.textView.setOnClickListener{
            onItemClicked(items,holder.adapterPosition)
        }
    }
}

问题原因

ItemTouchHelper在长按触发拖拽后,会将对应的ViewHolder标记为拖拽激活状态,此时执行移除操作,虽然数据已删除并调用了notifyItemRemoved,但RecyclerView不会正常回收这个处于拖拽状态的ViewHolder,导致视图残留。

修复方案

核心是在移除Item前先终止ItemTouchHelper的拖拽状态,确保ViewHolder能被正常回收。

修改步骤

  1. 将myItemTouchHelper改为Activity的成员变量,方便在移除回调中调用;
  2. 在移除Item的lambda中,先调用myItemTouchHelper.cancelDrag()终止拖拽,再处理数据和通知更新。

修改后的MainActivity代码

class MainActivity : AppCompatActivity() {
    private lateinit var myItemTouchHelper: ItemTouchHelper // 改为成员变量

    @SuppressLint("MissingInflatedId")
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val recyclerView = findViewById<RecyclerView>(R.id.recView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        val myAdapter = MyAdapter(layoutInflater,mutableListOf("A","B","C","D","E","F")){items,position->
            // 先终止拖拽状态
            myItemTouchHelper.cancelDrag()
            // 再执行移除操作
            items.removeAt(position)
            recyclerView.adapter?.notifyItemRemoved(position)
        }
        recyclerView.adapter =  myAdapter
        myItemTouchHelper = ItemTouchHelper(object : ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP or ItemTouchHelper.DOWN,0){
            override fun onMove(
                recyclerView: RecyclerView,
                viewHolder: ViewHolder,
                target: ViewHolder
            ): Boolean {
                val item = myAdapter.items[viewHolder.adapterPosition]
                myAdapter.items.removeAt(viewHolder.adapterPosition)
                myAdapter.items.add(target.adapterPosition,item)
                recyclerView.adapter?.notifyItemMoved(viewHolder.adapterPosition,target.adapterPosition)
                return true
            }

            override fun onSwiped(viewHolder: ViewHolder, direction: Int) {
            }
        })
        myItemTouchHelper.attachToRecyclerView(recyclerView)
    }
}

补充说明

如果不想暴露ItemTouchHelper给回调,也可以在Adapter中通过holder.itemView.isActivated判断当前是否处于拖拽状态(拖拽时ItemTouchHelper会将itemView设为activated),若处于拖拽状态则不执行移除操作,或者通过接口通知Activity终止拖拽后再移除。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 14:01:02