Recyclerview painfully slow to load cached images form Picasso

51,010

Solution 1

Setting recyclerView parameters as below solved my stutter problem :

recyclerView.setHasFixedSize(true);
recyclerView.setItemViewCacheSize(20);
recyclerView.setDrawingCacheEnabled(true);
recyclerView.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_HIGH);

i hope it helps someone else too.

Solution 2

Use 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.

Solution 3

I mixed the answer from @ergunkocak and @Mangesh Ghotage, using this works great:

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);

More about Picasso

Solution 4

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.

There is an alternate library i.e. "AQuery" which is very good for fetching and caching images from network.

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.

I resolved my issue by removing picaaso and using Aquery. Its just a one line replacement and you are done with your issue.

(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.

Solution 5

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.

Share:
51,010
charrondev
Author by

charrondev

Student, Designer, and Aspiring Full-Stack Dev. I have the Front-End Part down. I devour JavaScript and breathe CSS. Preferred technologies: SASS, Jade, Gulp, JS. Favorite software: Affinity Designer, Atom, Procreate, Tweetbot

Updated on July 09, 2022

Comments

  • charrondev
    charrondev almost 2 years

    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.

    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?

    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.