RecyclerView shuffles 动态添加 ImageViews



所以,我只是代码

override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
DownloadImageTask(holder.avatar).execute(mDataSet[position].avatar);
holder.header.setText(mDataSet[position].header)
holder.body.setText(mDataSet[position].body)

for (i in 0 until mDataSet[position].images.size){
val imgUrl= mDataSet[position].images.get(i)
var image= ImageView(holder.itemView.context)
image.layoutParams = ViewGroup.LayoutParams(200, 200)
image.maxHeight = 200
image.maxWidth = 200
val layout= holder.itemView.findViewById<View>(R.id.linear_layout)
DownloadImageTask(image).execute(imgUrl)
layout.linear_layout.addView(image)            
}
}

但是在视图中向下和向上滚动后,图像会在回收器视图中的任何项目之间随机播放。那么,如何解决它呢?

也不要忘记删除以前添加到linear_layout的图像视图。 尝试在取消下载过程后添加linear_layout.removeAllViews(),然后在开始新图像下载之前。

编辑:如果您更新为使用Glide..您的代码必须小于此:

override fun onBindViewHolder(
holder: ViewHolder,
position: Int
) {
Glide.with(holder.avatar.context)
.load(mDataSet[position].avatar)
.into(holder.avatar);
holder.header.setText(mDataSet[position].header)
holder.body.setText(mDataSet[position].body)
val layout= holder.itemView.findViewById<View>(R.id.linear_layout)
//cancel previous image download 
layout.linear_layout.children.toList().filter { it is ImageView }
.forEach { Glide.with(holder.itemView.context).clear(it) }
// remove image views
layout.linear_layout.removeAllViews()
//add row images
for (i in 0 until mDataSet[position].images.size){
val imgUrl= mDataSet[position].images.get(i)
var image= ImageView(holder.itemView.context)
image.layoutParams = ViewGroup.LayoutParams(200, 200)
image.maxHeight = 200
image.maxWidth = 200
Glide.with(holder.itemView.context).load(imgUrl).into(image)
layout.linear_layout.addView(image)            
}
}

注意:我尝试保留代码示例,但最好重用现有的动态图像视图并删除其余部分。

发生这种情况是因为相同的视图被重用,并且当视图快速滚动时,您在单个视图上有多个异步任务。如前面的答案库所述,如壁画,滑翔,凌空等会自动处理它。

在当前方案中解决此问题的最简单方法是将异步任务设置为视图中的标记,并在重用同一视图时取消在其上设置的先前异步任务。(请原谅java语法,我对Kotlin还不友好(

像这样:

AsyncTask prevTask = (AsyncTask) holder.avatar.getTag();
if(prevTask != null) {
prevTask.cancel();
}
AsyncTask task = DownloadImageTask(holder.avatar);
task.execute(mDataSet[position].avatar)
holder.avatar.setTag(task);

您必须为 for 循环中的图像编写类似的代码。

看起来您正在使用AsyncTask这似乎是罪魁祸首。 当您将以前创建的ViewHolders滚动到屏幕上时,RecyclerView会重新绑定它们,并且由于您似乎没有取消未完成的异步任务,因此您有一个争用案例。

可能是异步任务在重新绑定ViewHolder后完成,因此它会使用mDataSet中旧项目的图像更新ViewHolder。要解决此问题,您需要跟踪并取消异步任务,因为ViewHolders是绑定/取消绑定的。

更好的是,我建议使用像Glide这样的图像加载库。绑定ViewHolder后,您可以使用Glide.clear()取消之前对ImageViews的任何加载请求,并使用Glide.load(...).into(...)加载新图像。

最新更新