RecyclerView.Adapter 和 spanCount 大于 4 的 GridLayoutManager 连续重新创建 ViewHolder
RecyclerView.Adapter and GridLayoutManager with spanCount great than 4 continuously recreates ViewHolders
我在使用 GridLayoutManager 时遇到了 RecyclerView 的意外行为。如果 spanCount 大于 4,则 RecyclerView 在滚动时不断重新创建 ViewHolder。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = GridLayoutManager(this, 7)
recyclerView.adapter = Adapter()
}
private class Adapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
//continuously invokes while scrolling:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view:View = TextView(parent.context).apply {
text = "Hello!"
}
return object : RecyclerView.ViewHolder(view) {}
}
override fun getItemCount(): Int = 3500
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {}
}
如何修复它并强制 RecyclerView.Adapter 重用 ViewHolders?
我用这段代码重现了你的问题(可复制粘贴,不需要资源文件):
class RecActivity : AppCompatActivity() {
lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
recyclerView = RecyclerView(this)
setContentView(recyclerView)
recyclerView.layoutManager = GridLayoutManager(this, 7)
recyclerView.adapter = Adapter()
}
inner class Adapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var vhCount = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val txtView = AppCompatTextView(this@RecActivity)
txtView.tag = vhCount++.toString()
txtView.gravity = Gravity.CENTER
title = vhCount.toString() // display # of created VHs in title
return object : RecyclerView.ViewHolder(txtView){}
}
override fun getItemCount() = 3500
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder.itemView as TextView).text = "$position (${holder.itemView.tag})"
}
}
}
问题是没有足够的视图来填写 RecycledViewPool
中的整行。默认情况下,每个 ViewType 只有 5 个项目,因此在滚动时有 7 行的宽行会强制创建更多 ViewHolders
。要解决此问题,只需像这样增加 RecycledViewPool
的大小(在 onCreate
中):
recyclerView.layoutManager = GridLayoutManager(this, 7)
recyclerView.adapter = Adapter()
// add line below: 0 is default itemViewType, 14 is two rows of items which should be enough
recyclerView.recycledViewPool.setMaxRecycledViews(0, 14)
我在使用 GridLayoutManager 时遇到了 RecyclerView 的意外行为。如果 spanCount 大于 4,则 RecyclerView 在滚动时不断重新创建 ViewHolder。
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.main_activity)
val recyclerView = findViewById<RecyclerView>(R.id.recyclerView)
recyclerView.layoutManager = GridLayoutManager(this, 7)
recyclerView.adapter = Adapter()
}
private class Adapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
//continuously invokes while scrolling:
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val view:View = TextView(parent.context).apply {
text = "Hello!"
}
return object : RecyclerView.ViewHolder(view) {}
}
override fun getItemCount(): Int = 3500
override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {}
}
如何修复它并强制 RecyclerView.Adapter 重用 ViewHolders?
我用这段代码重现了你的问题(可复制粘贴,不需要资源文件):
class RecActivity : AppCompatActivity() {
lateinit var recyclerView: RecyclerView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
recyclerView = RecyclerView(this)
setContentView(recyclerView)
recyclerView.layoutManager = GridLayoutManager(this, 7)
recyclerView.adapter = Adapter()
}
inner class Adapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
var vhCount = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
val txtView = AppCompatTextView(this@RecActivity)
txtView.tag = vhCount++.toString()
txtView.gravity = Gravity.CENTER
title = vhCount.toString() // display # of created VHs in title
return object : RecyclerView.ViewHolder(txtView){}
}
override fun getItemCount() = 3500
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
(holder.itemView as TextView).text = "$position (${holder.itemView.tag})"
}
}
}
问题是没有足够的视图来填写 RecycledViewPool
中的整行。默认情况下,每个 ViewType 只有 5 个项目,因此在滚动时有 7 行的宽行会强制创建更多 ViewHolders
。要解决此问题,只需像这样增加 RecycledViewPool
的大小(在 onCreate
中):
recyclerView.layoutManager = GridLayoutManager(this, 7)
recyclerView.adapter = Adapter()
// add line below: 0 is default itemViewType, 14 is two rows of items which should be enough
recyclerView.recycledViewPool.setMaxRecycledViews(0, 14)