Dmi*_*try 10 android android-recyclerview linearlayoutmanager
当使用带有 LinearLayoutManager 和“reverseLayout”标志设置为 true 的 RecyclerView 时,当通过notifyItemChanged它通知任何项目时,也会调用onBindViewHolder第一个不可见的项目。之后它不会要求onViewRecycled该项目。因此,如果 ViewHolder 在 onBind 中进行某种订阅,它将永远不会被释放,因为不会调用 onRecycle。
这实际上看起来像是LinearLayoutManager. 如果您查看fillLinearLayoutManager中的方法,则有以下代码:
if (!layoutChunkResult.mIgnoreConsumed || layoutState.mScrapList != null || !state.isPreLayout()) {
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// we keep a separate remaining space because mAvailable is important for recycling
remainingSpace -= layoutChunkResult.mConsumed;
}
Run Code Online (Sandbox Code Playgroud)
据我所知,我们迭代子视图,直到我们填满所有需要的空间,换句话说layoutState.mAvailable,remainingSpace它们都以像素为单位。如果您进一步layoutChunk查看方法中发生的事情,您将看到这段代码:
// Consume the available space if the view is not removed OR changed
if (params.isItemRemoved() || params.isItemChanged()) {
result.mIgnoreConsumed = true;
}
Run Code Online (Sandbox Code Playgroud)
所以法学硕士将跳过任何项目FLAG_UPDATE,在我的情况下,是我要求的项目notifyItemChanged。跳过我的意思是项目的高度不会从这两个变量中减去:
layoutState.mAvailable -= layoutChunkResult.mConsumed;
// we keep a separate remaining space because mAvailable is important for recycling
remainingSpace -= layoutChunkResult.mConsumed;
Run Code Online (Sandbox Code Playgroud)
这将使循环迭代一个额外的视图。而且由于该视图未由 LLM 缓存(请参阅tryGetViewHolderForPositionByDeadline-> getScrapOrHiddenOrCachedHolderForPosition)(因为如果我没有弄错,它在屏幕边界之外)它将重新创建。但是在reverseLayout设置为 false(默认 LLM 状态)的情况下,它不会迭代它,因为它将首先到达列表的末尾:
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) { ... }
Namely here:
boolean hasMore(RecyclerView.State state) {
return mCurrentPosition >= 0 && mCurrentPosition < state.getItemCount();
}
Run Code Online (Sandbox Code Playgroud)
在reverseLayout== false 的情况下,我们从当前位置开始按升序迭代,即:
[ 0 ]
+--[ 1 ]--+
| [ 2 ] |
| [ 3 ] |
| [ 4 ] |
| [ 5 ] |
+--[ 6 ]--+
Run Code Online (Sandbox Code Playgroud)
假设我们调用notifyItemChanged位置为 3 的项目。因此 LLM 将迭代 1、2、(跳过 3)、4、5 和 6。由于它跳过了 3,因此将剩余像素来填充layoutState.mAvailable变量 BUT,因为我们处于循环结束,它将立即停止。
现在让我们看看当reverseLayout== true时会发生什么。
[ 6 ]
+--[ 5 ]--+
| [ 4 ] |
| [ 3 ] |
| [ 2 ] |
| [ 1 ] |
+--[ 0 ]--+
Run Code Online (Sandbox Code Playgroud)
因此,我们再次调用notifyItemChanged(3). LLM 将以相反的顺序开始迭代:0, 1, 2, (skip 3), 4 and 5. 然后因为它跳过了 3,所以仍然有像素要填充,我们不在列表的末尾,所以它将迭代 6 作为好。
最奇怪的是,在这个代码示例中,它只能在第一次长按时重现,之后第一个屏幕外视图onBind不会被调用。但是在发现这个东西的项目中,每次调用notifyItemChanged视图时它都是 100% 可重现的。
这是最小的可重现示例:
class MainActivity : AppCompatActivity() {
private val id = AtomicLong(0)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val images = (0..6).map {
return@map ImageItem(
"https://i.imgur.com/BBcy6Wc.jpg",
id.getAndIncrement()
)
}
recycler.layoutManager = LinearLayoutManager(this, RecyclerView.VERTICAL, true)
recycler.adapter = Adapter(images).apply { setHasStableIds(true) }
recycler.setRecyclerListener { viewHolder ->
if (viewHolder is Adapter.MyViewHolder) {
viewHolder.onRecycle()
}
}
recycler.adapter!!.notifyDataSetChanged()
}
data class ImageItem(val url: String, val id: Long)
class Adapter(
private val items: List<ImageItem>
) : RecyclerView.Adapter<Adapter.MyViewHolder>(), OnRecyclerItemClick {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
println("TTTAAA onCreateViewHolder")
val view = LayoutInflater.from(parent.context)
.inflate(R.layout.my_view_holder, parent, false)
return MyViewHolder(view, this)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder as MyViewHolder
holder.onBind(items[position])
}
override fun getItemCount(): Int {
return items.size
}
override fun getItemId(position: Int): Long {
return items[position].id
}
override fun onLongClick(position: Int) {
println("TTTAAA onLongClick($position)")
notifyItemChanged(position)
}
class MyViewHolder(
private val view: View,
private val callback: OnRecyclerItemClick
) : RecyclerView.ViewHolder(view) {
private val imageView: AppCompatImageView = view.findViewById(R.id.my_image)
fun onBind(imageItem: ImageItem) {
println("TTTAAA onBind $layoutPosition")
imageView.setOnLongClickListener {
callback.onLongClick(layoutPosition)
return@setOnLongClickListener true
}
Glide.with(imageView.context)
.load(imageItem.url)
.centerCrop()
.into(imageView)
}
fun onRecycle() {
println("TTTAAA onRecycle $layoutPosition")
imageView.setOnClickListener(null)
Glide.with(imageView.context)
.clear(imageView)
}
}
}
}
interface OnRecyclerItemClick {
fun onLongClick(position: Int)
}
Run Code Online (Sandbox Code Playgroud)
这是日志:
onLongClick(0)
onCreateViewHolder
onBind 3
onCreateViewHolder
onBind 0
onRecycle 0
onLongClick(0)
onBind 0
onRecycle 0
onLongClick(0)
onBind 0
onRecycle 0
Run Code Online (Sandbox Code Playgroud)
我正在长按位置 0 的视图,位置 3(屏幕外)的附加视图也被绑定。
| 归档时间: |
|
| 查看次数: |
424 次 |
| 最近记录: |