0

I want to load images I have in my recyclerview after 350ms and I think I'm using wrong method for that. This is my code:

@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
    holder.songView.setText(objects_.get(position).getAlbum());
    holder.artistView.setText(objects_.get(position).getArtist());
    holder.cover.setImageDrawable(context.getResources().getDrawable(R.drawable.song));
    if(holder.r!=null){
        handler.removeCallbacks(holder.r);
    }
    holder.r = new Runnable() {
        @Override
        public void run() {
            Drawable img = Drawable.createFromPath(objects_.get(position).getCover());
            if (img != null) {
                holder.cover.setImageDrawable(img);
                Bitmap bitmap = ((BitmapDrawable) img).getBitmap();
                Palette palette = Palette.from(bitmap).generate();
                Palette.Swatch p = palette.getVibrantSwatch();
                if (p != null) {
                    holder.albumholder.setBackgroundColor(p.getRgb());
                }
            }
        }
    };
    handler.postDelayed(holder.r,300);
}

But I have a problem with this. when I fast scroll recyclerview images of previous items loads at first then changes to new items picture. You can see result in GIF from this link:

http://8pic.ir/images/nkaaeqdvigqy4c6g2h5n.gif

what can I do to fix it?

Amir_P
  • 8,322
  • 5
  • 43
  • 92

3 Answers3

3

I don't understand why do you need this 350ms delay but if you want to do it try some other approach:

Your problem is linked to the fact that RecyclerView recycles (suprise...) item views instead of creating new. That means that you will see previously load image, and if you have posted delayed task (handler.postDelayed(...)) it will be executed event if view was recycled, so wrong image can be loaded for particular list item.

General problem is that you're doing to much work in your onBindViewHolder. You should try to reduce computations here, or at least try to move them to some separate thread (handler is using this same thread it was created - in this case the UI thread).

  1. Create handler inside view holder instead of inside your adapter.
  2. Set some placeholder as an image
  3. clear tasks (messages) currently waiting to be executed:

holder.handler.removeCallbacksAndMessages(null);

  1. post load task (handler.postDelayed(...))

It's also possible that all you need is some nice image loading library like Picasso.

piotrpo
  • 12,398
  • 7
  • 42
  • 58
  • where should i remove callbacks and messages? how to post load task? and picasso can load image from device storage? I've searched for that and it's not possible with picasso – Amir_P Apr 13 '16 at 10:06
  • and i want 350ms because it is laggy without that – Amir_P Apr 13 '16 at 10:23
  • Yes, picasso can load images from local storage. I've put some more explanations. – piotrpo Apr 13 '16 at 10:45
1

As we know recycler view reuse same view during scroll so it is displaying older images while you lazy load for some moments and after that it will update your imageview.

Solution is simply reset your imageview to default( ie white background or default image) state before lazyload .

You should avoid setimageresource() ,instead use setimagedrawable()

setImageResource Vs setDrawable

Community
  • 1
  • 1
Alok
  • 881
  • 1
  • 11
  • 18
  • I'm doing that with this code `holder.cover.setImageResource(R.drawable.song);` but it's not fixing my problem – Amir_P Apr 13 '16 at 04:42
  • You should avoid setimageresource ,instead use setimagedrawsble http://stackoverflow.com/questions/9774705/setimageresource-vs-setdrawable – Alok Apr 13 '16 at 04:58
  • thanks for mentioning that was helpful. but problem is still – Amir_P Apr 13 '16 at 05:04
0

Instead of using the runnable for loading images, use AsyncTask. You'll need to execute a separate AsyncTask for each image. This AsyncTask will be saved as a WeakReference inside the drawable object which will be set in the respective ImageView.

WeakReferences are used for mapping purposes. The advantage of using WeakReferences is that the entries can be removed from the memory as soon as they are not required by your app. They will be removed by the Garbage Collector. We need to use the WeakReferences because there can be a large number of AsyncTasks getting executed (equal to the number of items present in the RecyclerView) at the same time and Android system or your app will not be able to identify which AsyncTask belongs to which ImageView without these references.

Now, as the drawable is set in the ImageView, it will contain the WeakReference to its respective AsyncTask. This AsyncTask will process the respective bitmap or drawable to be set on the ImageView and all this will be done off the UI thread.

In order to set the AsyncTask in the drawable object, you'll need to create a custom drawable class which will work as a Drawable object but will have the benefit of attaching an AsyncTask to it.

This Drawable object and AsyncTask will take care of loading the images.

The complete explanation and code for this concept has been provided on Android Developers website. Visit the link: http://developer.android.com/training/displaying-bitmaps/process-bitmap.html

Varun Kumar
  • 1,241
  • 1
  • 9
  • 18