我正在使用RecyclerView
来显示包含imageView
的列表。为了使UI更流畅,我将保存在SD卡上的58dp缩略图加载到这些具有asyncTask
的imageViews中。
问题是,一旦childView
进入可视化显示,其他数据中的旧图像将被重用,然后在AsyncTask
完成后替换。我可以通过在imageView
中将onPreExecute
位图设置为null来停止改组。
有没有办法真正重复使用旧图像,或者每次新的View
到位时我是否真的需要从SD卡加载图像?这使得视图非常难看,因为首先存在错误的图像或者图像是纯白色的。
答案 0 :(得分:32)
由于视图重用,您将使用已有内容的视图获取视图,如果您使用的是ListViews
模式,这也是ViewHolder
的问题。
这里有两个解决方案,好的做法和糟糕的黑客攻击:
在良好实践中,您将ImageView
设置为不显示任何内容
bindViewHolder(VH holder, int position)
开始使用
setDrawable(null)
或类似。
在糟糕的黑客行为中,您不会回收/重复使用视图,不会强制执行ViewHolder
模式,并且每次都会对其进行充气,但这只是允许的在ListView
和其他旧组件中。
答案 1 :(得分:8)
您应该查看universal image loader。 它具有内存缓存,磁盘缓存,并且它异步加载您的图像,因此不会阻止ui。您可以设置默认图像和/或无法获取图像等。它可以对图像进行采样以减少位图的内存占用。我真的建议你用它来拍照。
不要为您的情况禁用可回收,因为它毫无意义。必须回收图像,因为如果没有正确采样,它们的位图可绘制会产生非常高的内存过载。
RecyclerViewAdapter中的示例用法:
@Override
public void onBindViewHolder(CustomViewHolder viewHolder, int position) {
String imageUri = "";//local or remote image uri address
//viewHolder.imgView: reference to your imageview
//before you call the displayImage you have to
//initialize imageloader in anywhere in your code for once.
//(Generally done in the Application class extender.)
ImageLoader.getInstance().displayImage(imageUri, viewHolder.imgView);
}
编辑: 如今,我认为Glide是我的主要图像加载和缓存库。 您可以像这样使用它:
Glide.with(context)
.load(imageUri)
.placeholder(R.drawable.myplaceholder)
.into(imageView);
答案 2 :(得分:4)
您应该在开始新请求之前取消旧请求,但无论是否取消,如果两个图像在同一个容器/视图持有者上同时或多或少地加载,则仍然可以显示错误的图像(发生轻松快速滚动和小图像。)
解决方案是:
使用RxJava从后台应用加载图标的示例:
public void loadIcon(final ImageView appIconView, final ApplicationInfo appInfo, final String uniqueAppID) {
Single.fromCallable(() -> {
return appIconView.getContext().getPackageManager().getApplicationIcon(appInfo);
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( drawable -> {
if (uniqueAppID.equals(mUniqueAppID)) { // Show always the correct app icon
appIconView.setImageDrawable(drawable);
}
}
);
}
此处mUniqueAppID是onBindViewHolder更改的视图持有者的字段
答案 3 :(得分:1)
您必须在" onBindViewHolder"中取消旧请求方法:
try{
((SpecialOfferViewHolder)viewHolder).imageContainer.cancelRequest();
}catch(Exception e) {
}
请记得在viewHolder中保存图像容器:
public void onResponse(ImageContainer response, boolean arg1) {
((SpecialOfferViewHolder)viewHolder).imageContainer=response;
}
答案 4 :(得分:0)
MLProgrammer所说的是非常正确的。
解决方案,幸运的是,这很简单:停止回收
holder.setIsRecyclable(false);
这种选择有其后果,因为做我上面建议的做法会抑制RecyclerView的主要目的之一:回收。
答案 5 :(得分:0)
我想补充一下好习惯:
在良好实践中,您将ImageView设置为在bindViewHolder(VH holder,int position)的开头使用setDrawable(null)或类似内容显示任何内容。
不显示任何内容,而是显示加载程序图像,以便向用户提供反馈,以便在该视图中进行某些处理,并在一段时间内看到结果。只看空白视图不是一种好习惯,您需要向用户提供反馈。
答案 6 :(得分:-1)
您应该使用Picasso。 适用于Android的强大的图片下载和缓存库。 易于使用和强大的库。使用此库,您可以从资源,资产,文件,内容提供程序异步或同步地获取图像。