解决RecyclerView的onCreateViewHolder一直被调用的问题

806 阅读3分钟

在使用 RecyclerView 时,onCreateViewHolder 不断被调用通常意味着 RecyclerView 视图的回收和复用机制出现了问题。这种情况通常是由以下几个原因之一引起的:

  1. RecyclerView 的高度没有正确设置:如果 RecyclerView 的高度设置为 wrap_content 或者没有正确测量,可能会导致 RecyclerView 不断请求新的视图。
  2. Adapter 数据更新不当:如果在 Adapter 数据更新时使用了错误的方法,比如不断调用 notifyDataSetChanged(),而不是 notifyItemChanged() 或其他更细粒度的方法,也会导致 onCreateViewHolder 不断被调用。
  3. ViewType 设置错误:如果 getItemViewType 返回了不断变化的值,RecyclerView 会认为每个 item 都是不同的类型,因此不会复用 ViewHolder

下面详细分析这些原因,并提供相应的解决方法:

一、确保 RecyclerView 的高度正确设置

在布局文件中,确保 RecyclerView 的高度没有设置为 wrap_content。推荐的做法是给 RecyclerView 设置一个明确的高度,比如 match_parent 或者固定高度。

xml
复制代码
<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recyclerView"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />

如果你需要使用 wrap_content,请确保它的父视图的测量方式是合理的。

二、正确更新 Adapter 数据

在更新 Adapter 的数据时,尽量使用细粒度的更新方法,而不是每次都调用 notifyDataSetChanged()。例如:

kotlin
复制代码
adapter.notifyItemInserted(position)
adapter.notifyItemRemoved(position)
adapter.notifyItemChanged(position)
adapter.notifyItemRangeChanged(positionStart, itemCount)

如果确实需要刷新整个数据集,可以在数据集发生变化前后打印日志,检查数据集的变化过程。

三、确保 ViewType 的正确设置

确保 getItemViewType 返回的值在数据集中的相同位置是一致的。例如:

kotlin
复制代码
override fun getItemViewType(position: Int): Int {
    // 返回稳定的 ViewType,确保同一位置的 ViewType 一致
    return items[position].viewType
}

四、检查 ViewHolder 的创建和绑定逻辑

onCreateViewHolder 中只做与创建视图相关的操作,在 onBindViewHolder 中做数据绑定的操作。避免在 onCreateViewHolder 中做耗时操作。

kotlin
复制代码
class MyAdapter(private val items: List<MyItem>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(items[position])
    }

    override fun getItemCount(): Int = items.size

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val textView: TextView = itemView.findViewById(R.id.textView)

        fun bind(item: MyItem) {
            textView.text = item.text
        }
    }
}

五、示例代码

结合以上几点,提供一个完整的示例代码:

kotlin
复制代码
data class MyItem(val id: Long, val text: String, val viewType: Int)

class MyAdapter(private val items: List<MyItem>) : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

    init {
        setHasStableIds(true)
    }

    override fun getItemId(position: Int): Long {
        return items[position].id
    }

    override fun getItemViewType(position: Int): Int {
        return items[position].viewType
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
        return MyViewHolder(view)
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        holder.bind(items[position])
    }

    override fun getItemCount(): Int = items.size

    class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val textView: TextView = itemView.findViewById(R.id.textView)

        fun bind(item: MyItem) {
            textView.text = item.text
        }
    }
}

// 在 Activity 或 Fragment 中使用 RecyclerView
class MyActivity : AppCompatActivity() {

    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: MyAdapter

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        recyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        
        val items = listOf(
            MyItem(1, "Item 1", 0),
            MyItem(2, "Item 2", 0),
            MyItem(3, "Item 3", 0)
        )
        adapter = MyAdapter(items)
        recyclerView.adapter = adapter
    }
}

通过上述方法,你可以有效地减少 onCreateViewHolder 的调用次数,从而解决 RecyclerView 中的性能问题。如果问题仍然存在,请进一步检查你的 RecyclerViewAdapter 的实现,确保没有其他问题导致视图不断创建。