Java Recyclerview 从 Picasso 加载缓存图像的速度非常缓慢
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/26856487/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Recyclerview painfully slow to load cached images form Picasso
提问by charrondev
I have implemented a RecyclerView that contains mainly images which is loading in through Picasso. My problem is that as soon as I scroll down or up the view, the placeholder image appears for approx. a second before it is replaced by the actual image. It scrolls very smoothly, but is practically unusable. This happens every time something scrolls off the screen.
我已经实现了一个 RecyclerView,其中主要包含通过 Picasso 加载的图像。我的问题是,一旦我向下或向上滚动视图,占位符图像就会出现大约。在它被实际图像替换之前的一秒钟。它滚动非常流畅,但实际上无法使用。每次有东西滚出屏幕时都会发生这种情况。
Picasso is clearly caching the images to the disk, as they load faster than the initial load time, but they are definitely instantly reloading from the cache. What did i implement incorrectly?
Picasso 显然将图像缓存到磁盘,因为它们的加载速度比初始加载时间快,但它们肯定会立即从缓存中重新加载。我错误地执行了什么?
My adapter code:
我的适配器代码:
public class ExploreAdapter extends RecyclerView.Adapter<ExploreAdapter.ExploreViewHolder> {
private List<ExploreItem> exploreItemList;
private Context mContext;
private int deviceWidth = PrefUtils.getDeviceWidth();
public ExploreAdapter(Context context, List<ExploreItem> exploreItemList){
this.mContext = context;
this.exploreItemList = exploreItemList;
}
@Override
public int getItemCount(){
return exploreItemList.size();
}
@Override
public void onBindViewHolder(ExploreViewHolder exploreViewHolder, int i){
ExploreItem item = exploreItemList.get(i);
exploreViewHolder.getvTitle().setText(item.getTitle());
exploreViewHolder.getvUsername().setText(item.getUsername());
exploreViewHolder.getvNumLikes().setText(item.getNbOfLikes());
Picasso.with(mContext).load(item.getMediaLocation().trim())
.resize(deviceWidth, deviceWidth)
.centerCrop()
.placeholder(R.drawable.profile_background)
.into(exploreViewHolder.getvImage());
Picasso.with(mContext).load(item.getUserProfilePicUrl().trim())
.placeholder(R.drawable.profile_picture)
.into(exploreViewHolder.getvProfilePic());
}
@Override
public ExploreViewHolder onCreateViewHolder(ViewGroup viewGroup, int i){
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.explore_item, viewGroup, false);
return new ExploreViewHolder(itemView);
}
public static class ExploreViewHolder extends RecyclerView.ViewHolder{
private TextView vTitle,
vUsername,
vNumLikes;
private SquareImage vImage;
private ImageView vProfilePic;
public ExploreViewHolder(View v){
super(v);
this.vTitle = (TextView) v.findViewById(R.id.explore_item_title);
this.vUsername = (TextView) v.findViewById(R.id.explore_item_username);
this.vNumLikes = (TextView) v.findViewById(R.id.explore_item_number_likes);
this.vImage = (SquareImage) v.findViewById(R.id.explore_item_image);
this.vProfilePic = (ImageView) v.findViewById(R.id.explore_item_profile_picture);
}
public TextView getvTitle() {
return vTitle;
}
public TextView getvUsername() {
return vUsername;
}
public ImageView getvProfilePic() {
return vProfilePic;
}
public SquareImage getvImage() {
return vImage;
}
public TextView getvNumLikes() {
return vNumLikes;
}
public void setvImage(SquareImage vImage) {
this.vImage = vImage;
}
public void setvNumLikes(TextView vNumLikes) {
this.vNumLikes = vNumLikes;
}
public void setvProfilePic(ImageView vProfilePic) {
this.vProfilePic = vProfilePic;
}
public void setvTitle(TextView vTitle) {
this.vTitle = vTitle;
}
public void setvUsername(TextView vUsername) {
this.vUsername = vUsername;
}
}
}
Any help is appreciated.
任何帮助表示赞赏。
回答by Jorge Antonio Díaz-Benito
I can't verify the correctness of this solution, but I was facing this problem as well and approached it following this idea:
我无法验证此解决方案的正确性,但我也遇到了这个问题,并按照以下想法进行了处理:
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int i) {
...
if(mImageView.getDrawable() == null)
Picasso.with(context)
.load(path)
.error(errorResId)
.tag(tag)
.into(mImageView);
...
}
It is intended to forbid Picasso from loading anything unless the ImageView has nothing to display.
它旨在禁止毕加索加载任何内容,除非 ImageView 没有任何内容可显示。
回答by Daniele Segato
Picasso automatically cache images in two levels:
Picasso 自动在两个级别缓存图像:
- disk cache (this is actually NOT in picasso but in the network layer that picasso use)
- memory cache
- 磁盘缓存(这实际上不在 picasso 中,而是在 picasso 使用的网络层中)
- 内存缓存
They are bot initialized with defaults that suites most applications.
它们使用适合大多数应用程序的默认值进行机器人初始化。
When the memory cache is getting too big the oldest used bitmap is removed from memory cache (which by default is 1/7 of the available heap space).
当内存缓存变得太大时,最早使用的位图将从内存缓存中删除(默认情况下是可用堆空间的 1/7)。
I think what's happening here is that you are close to the memory limit of the cache and thus images are decoded again every time you need them again.
我认为这里发生的事情是您接近缓存的内存限制,因此每次再次需要图像时都会再次解码图像。
See here: Android Picasso Configure LruCache Size
请参阅此处: Android Picasso 配置 LruCache 大小
But I advice AGAINST increasing the memory cache size unless you really are re-sizing the images to the actual size you use..
但我建议反对增加内存缓存大小,除非您确实将图像调整为您使用的实际大小。
I think the problem with your code is this:
我认为你的代码的问题是这样的:
.resize(deviceWidth, deviceWidth)
are you sure you really need an image of that size? What do you store in PrefUtils ? does it get updated when the device rotate? (landscape vs portrait)
你确定你真的需要这么大的图片吗?您在 PrefUtils 中存储了什么?设备旋转时它会更新吗?(横向 vs 纵向)
If you need smaller images try to pass the resize the actual size of the image you are gonna use. If you don't know (computed at runtime with the layout) do with an approximation or write your code to obtain the actual size (I usually write custom classes extending ImageView for these stuff).
如果您需要较小的图像,请尝试通过调整大小来调整您将要使用的图像的实际大小。如果您不知道(在运行时使用布局计算),请使用近似值或编写代码以获得实际大小(我通常为这些内容编写扩展 ImageView 的自定义类)。
If you really need the images at that size just increase the cache size (see link above) and you should be good to go.
如果您确实需要该大小的图像,只需增加缓存大小(请参阅上面的链接),您就可以开始使用了。
回答by Faizan Tariq
I have encountered this issue recently, and yes you are right. Picasso is a good library for showing images from sdcard but it has some issues regarding cache if you are fetching images online that is why you see a place holder image while scrolling. I tried different things and the issue was not resolved.
我最近遇到了这个问题,是的,你是对的。Picasso 是一个很好的库,用于显示 sdcard 中的图像,但如果您在线获取图像,它会存在一些有关缓存的问题,这就是为什么您在滚动时看到占位符图像的原因。我尝试了不同的事情,但问题没有解决。
There is an alternate library i.e. "AQuery" which is very good for fetching and caching images from network.
有一个替代库,即“AQuery”,它非常适合从网络获取和缓存图像。
http://code.google.com/p/android-query/
http://code.google.com/p/android-query/
The main advantages of AQuery/Android Query is that you can clear the cache, no lag while scrolling and quite effective for caching images and not showing place holder images while scrolling.
AQuery/Android Query 的主要优点是可以清除缓存,滚动时没有延迟,对于缓存图像和滚动时不显示占位符图像非常有效。
I resolved my issue by removing picaaso and using Aquery. Its just a one line replacement and you are done with your issue.
我通过删除 picaaso 并使用 Aquery 解决了我的问题。它只是一行替换,您的问题就解决了。
(new AQuery(mContext)).id(exploreViewHolder.getvProfilePic()).image(item.getUserProfilePicUrl().trim(), true, true, device_width, R.drawable.profile_background, aquery.getCachedImage(R.drawable.profile_background),0);
Besides you can easily shift from picasso to AQuery as there is no such thing which is available in picasso but not in AQuery and the syntax is almost the same. So I would recommend you to use AQuery, until this is fixed by picasso.
此外,您可以轻松地从 picasso 转换到 AQuery,因为在 picasso 中没有这样的东西,但在 AQuery 中没有,而且语法几乎相同。所以我建议你使用 AQuery,直到毕加索解决这个问题。
回答by Mangesh
Use fit()
.
使用fit()
.
Picasso.with(context)
.load(file)
.fit()
.centerCrop()
.into(imageView);
The fit()
actually resizes the image to fit into the imageView
's bounds. This doesn't load the full image and smoothens the scrolling.
在fit()
实际调整大小以适应该图像imageView
的边界。这不会加载完整图像并平滑滚动。
回答by ergunkocak
Setting recyclerView parameters as below solved my stutter problem :
如下设置 recyclerView 参数解决了我的口吃问题:
recyclerView.setHasFixedSize(true);
recyclerView.setItemViewCacheSize(20);
recyclerView.setDrawingCacheEnabled(true);
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);
i hope it helps someone else too.
我希望它也能帮助别人。
回答by pichon
just use noPlaceholder
attribute.
只需使用noPlaceholder
属性。
回答by lisandro101
I mixed the answer from @ergunkocak and @Mangesh Ghotage, using this works great:
我混合了@ergunkocak 和@Mangesh Ghotage 的答案,使用这个效果很好:
recyclerView.setHasFixedSize(true);
recyclerView.setItemViewCacheSize(20);
recyclerView.setDrawingCacheEnabled(true);
And this on every image:
这在每张图片上:
Picasso.with(context)
.load(file)
.fit()
.into(imageView);
besides you could set Picasso to use a single instance like this:
此外,您可以将毕加索设置为使用这样的单个实例:
Picasso.setSingletonInstance(picasso);
And finally enabling the cache indicators will give you clues on what's going wrong:
最后启用缓存指示器将为您提供有关问题的线索:
Picasso
.with(context)
.setIndicatorsEnabled(true);
回答by Rana Asad
The best answer of this question is do not use round image view just use transformation with simple image view because of round image view the application takes a lot of memory just use simple image view and set transformation with it. Use this transformation
这个问题的最佳答案是不要使用圆形图像视图,只使用简单图像视图的变换,因为圆形图像视图应用程序占用大量内存,只需使用简单图像视图并用它设置变换。 使用此转换
回答by j-j
Use Glide. it caches the resized image, not the full-size image, unlike Picasso. In this approach, you will lose image quality. If you want to retain the full-size quality, I suggest you use Glide to load and save the image into file storage, so whenever you need the full-size image, just access that image from file storage.
使用滑翔。与 Picasso 不同,它缓存调整后的图像,而不是全尺寸图像。在这种方法中,您将失去图像质量。如果你想保留全尺寸质量,我建议你使用 Glide 将图像加载并保存到文件存储中,所以当你需要全尺寸图像时,只需从文件存储中访问该图像。