RecyclerView Adapter滚动时重复添加TextView问题求助
解决RecyclerView滚动后标签重复显示的问题
嘿,这个问题我太熟了!核心原因就是RecyclerView的ViewHolder复用机制在搞事情,咱们一步步来搞定它:
问题根源
当RecyclerView滚动时,滑出屏幕的ViewHolder会被回收,重新分配给刚进入屏幕的新item。如果你的Adapter在onBindViewHolder方法里,每次都直接往标签容器(比如LinearLayout)里添加新的TextView标签,但没有先清空容器里之前的内容,那复用的时候旧标签就会保留,新标签又叠加上,自然就出现了滚动一次多一组标签的情况。
快速解决方案:绑定前清空容器
这是最直接的修复方式,在绑定当前item的数据之前,先把标签容器里的所有子View清除干净,再添加当前item的标签。
假设你的ViewHolder里有一个LinearLayout用来装标签,代码示例如下:
class VideoAdapter(private val _c: Context, private val dataList: List<VideoItem>) : RecyclerView.Adapter<VideoAdapter.ViewHolder>() { inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tagContainer: LinearLayout = itemView.findViewById(R.id.tag_container) // 其他控件... } override fun onBindViewHolder(holder: ViewHolder, position: Int) { // 关键步骤:先清空标签容器,避免复用旧标签 holder.tagContainer.removeAllViews() // 处理当前item的标签数据 val currentItem = dataList[position] currentItem.tags.forEach { tagText -> val tagTv = TextView(_c).apply { text = tagText // 设置标签的样式(背景、内边距等) setBackgroundResource(R.drawable.tag_background) setPadding(dp2px(8), dp2px(4), dp2px(8), dp2px(4)) layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT ).apply { marginEnd = dp2px(4) } } holder.tagContainer.addView(tagTv) } // 其他绑定逻辑... } // 辅助方法:dp转px private fun dp2px(dp: Int): Int { return (_c.resources.displayMetrics.density * dp).toInt() } // 其他必要方法(onCreateViewHolder、getItemCount)... }
进阶优化:复用标签控件(提升性能)
如果你的标签数量有上限(比如最多显示3个),可以在ViewHolder初始化时预先创建好标签TextView,绑定数据时只更新内容和显示状态,避免频繁创建和销毁View,提升滚动流畅度:
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tagContainer: LinearLayout = itemView.findViewById(R.id.tag_container) // 预先创建最多3个标签控件 private val tagViews = mutableListOf<TextView>() init { repeat(3) { val tagTv = TextView(itemView.context).apply { setBackgroundResource(R.drawable.tag_background) setPadding(dp2px(8), dp2px(4), dp2px(8), dp2px(4)) layoutParams = LinearLayout.LayoutParams( LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT ).apply { marginEnd = dp2px(4) } visibility = View.GONE // 默认隐藏 } tagViews.add(tagTv) tagContainer.addView(tagTv) } } // 对外提供绑定标签的方法 fun bindTags(tags: List<String>) { // 先隐藏所有标签 tagViews.forEach { it.visibility = View.GONE } // 给需要显示的标签设置内容并显示 tags.forEachIndexed { index, tagText -> if (index < tagViews.size) { tagViews[index].apply { text = tagText visibility = View.VISIBLE } } } } } // 在onBindViewHolder里调用: override fun onBindViewHolder(holder: ViewHolder, position: Int) { val currentItem = dataList[position] holder.bindTags(currentItem.tags) // 其他绑定逻辑... }
重要注意事项
- 永远记住:ViewHolder是复用的,所有随item变化的内容(比如标签、图片、选中状态、文本内容等),在绑定数据时都要做重置或更新处理,不能依赖ViewHolder的初始状态。
- 如果是用动态添加View的方式展示可变内容,一定要先清空容器再添加新内容,否则旧内容会残留导致重复显示。
内容的提问来源于stack exchange,提问作者Ali
相关产品推荐
相关产品推荐

