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能被正常回收。
修改步骤
- 将
myItemTouchHelper改为Activity的成员变量,方便在移除回调中调用; - 在移除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
相关产品推荐
相关产品推荐

