22

I am loading 400x200 images in RecyclerView, but scrolling is laggy on 2k devices. I am using Picasso for loading images from resource.

As you can see in the demo images are blurry on 2k screen, but if I load higher resolution images the situation gets worse. How to fix this, I am not even loading large image, its 400x200 ?

Demo

Here is my code card_view.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/card_view"
    card_view:cardPreventCornerOverlap="false"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:layout_margin="8dp"
    card_view:cardCornerRadius="2dp">

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:id="@+id/rel">

        <ImageView
            android:id="@+id/cardimage"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:scaleType="fitXY"
            android:src="@drawable/p7"/>

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="title"
            android:paddingLeft="16dp"
            android:paddingRight="16dp"
            android:paddingTop="16dp"
            android:paddingBottom="24dp"
            android:textStyle="bold"
            android:textSize="24sp"
            android:id="@+id/cardtitle"
            android:layout_gravity="center_vertical"/>

    </LinearLayout>

</android.support.v7.widget.CardView>

Myadapter Code

public class CardAdapter extends RecyclerView.Adapter<CardAdapter.ViewHolder> {

private Context mContext;
List<Flower> list = new ArrayList<>();

public CardAdapter(Context mContext, List<Flower> list) {
    this.mContext = mContext;
    this.list = list;

}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType)   {

    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.card_view, parent, false);
    return new ViewHolder(itemView);
}

@Override
public void onBindViewHolder(ViewHolder holder, int position)  {

    holder.Flower=getItem(position);
    holder.cardtitle.setText(list.get(position).name);

    Picasso.with(mContext)
            .load(list.get(position).id)
            .placeholder(R.drawable.ic_launcher)
            .into(holder.cardimage);
}

@Override
public void onAttachedToRecyclerView(RecyclerView recyclerView) {
    super.onAttachedToRecyclerView(recyclerView);
}

@Override
public int getItemCount() {
    return list.size();
}

public Flower getItem(int i) {
    return list.get(i);
}

public class ViewHolder extends RecyclerView.ViewHolder {

    ImageView cardimage;
    TextView cardtitle;
    Flower Flower;

    public ViewHolder(View itemView) {
        super(itemView);

        cardimage = (ImageView) itemView.findViewById(R.id.cardimage);
        cardtitle = (TextView) itemView.findViewById(R.id.cardtitle);
    }
}
}

UPDATE: I am loading images from resource, I am not downloading nothing

Here is my array

 private void initializeData() {
    flowers = new ArrayList<>();
    flowers.add(new Flower("Flower 1", R.drawable.p8));
    flowers.add(new Flower("Flower 2", R.drawable.p10));
    flowers.add(new Flower("Flower 3", R.drawable.p11));
    flowers.add(new Flower("Flower 4", R.drawable.p8));
    flowers.add(new Flower("Flower 5", R.drawable.photo2));
    flowers.add(new Flower("Flower 6", R.drawable.photo6));
    flowers.add(new Flower("Flower 7", R.drawable.p12));
    flowers.add(new Flower("Flower 8", R.drawable.p9));
    flowers.add(new Flower("Flower 9", R.drawable.p8));
    flowers.add(new Flower("Flower 10", R.drawable.p8));
    flowers.add(new Flower("Flower 11", R.drawable.p8));
    flowers.add(new Flower("Flower 12", R.drawable.p10));
}

UPDATE 2 : Guys I fixed most of the lag by setting adapter.setHasStableIds(true) , but app is still laggy on the first scroll while images are not loaded yet, how to fix that ?

UPDATE 3: I just tried loading images from web and everything seems smooth, probably there is some problem with loading images from resource .

Ok, thank you guys, I am gonna load my images from web.

Community
  • 1
  • 1
phlosopher_mk
  • 330
  • 1
  • 3
  • 11

14 Answers14

23

if you are using two or more recycler views like (RecyclerView and NestedView) try this

recyclerView.setNestedScrollingEnabled(false);

Source : Recyclerview inside Nested Scrollview scroll but does not fast scroll like normal Recyclerview or Nested Scrollview

Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
Dasser Basyouni
  • 2,614
  • 3
  • 20
  • 43
11

Is RecyclerView.setHasFixedSize() set to true? I'm not able to reproduce this lag...can you please post the whole project on git? Check this out, maybe it can help you: http://antonioleiva.com/recyclerview/

Vadim Kotov
  • 7,103
  • 8
  • 44
  • 57
Andrei Verdes
  • 583
  • 4
  • 22
  • Yes is true. Here is android studio project . https://drive.google.com/file/d/0BypaioeS7MXPNHY3ZUFiNVBtcTg/view?usp=sharing – phlosopher_mk Oct 22 '15 at 22:55
  • 3
    I'm using a genymotion vm and I can't seem to reproduce the lag. I have 3 possible improvements: 1. in activity_main.xml, change RecyclerView.layout_height to "match_parent" 2. Try using only pngs in your drawable folder, as they decompress faster 3. Set adapter.setHasStableIds(true); and in CardAdapter add: `@Override public long getItemId(int position) { return position; }` – Andrei Verdes Oct 23 '15 at 00:04
  • Thank you so much, that fixed most of the lag. Now app is laggy only on the first scroll when images are still not loaded. Once when images are loaded everything is smooth. Any thoughts about this ? Are you running emulator on 2k resolution, try running n6 or n6p . Its laggy only on 2k resolution. – phlosopher_mk Oct 23 '15 at 01:48
  • Yes...I tried on nexus 6 emulator. I'll try nexus 6p. One of my friends suggested you to try Glide instead of Picasso, let me know how it goes. https://github.com/bumptech/glide – Andrei Verdes Oct 24 '15 at 10:33
11

I Believe that sometimes you may piss off when only trying some static images in drawable to display your recycler view. And it happens extremely lagging.

Why? You may not get this issue when using some library to load an image from url (Picasso, Glide, ...), but what happens with the same image, the same size, and it's right in your drawable (not need to download at all). And after a while, I figure out, android did some trick to resize an image in drawable for us to get a proper image in different resolution devices. So my suggestion is to use drawable-nodpi to store your image in order for android not to interfere with our images.

Nguyen Tan Dat
  • 1,862
  • 1
  • 18
  • 16
  • I have tried all other methods but didn't work, but this right here is the solution. Thanks a lot!! – Nuhman Sep 16 '18 at 14:56
  • This is what is the crux of the problem, especially when you have different viewholders in same adapter. – yUdoDis Oct 17 '18 at 11:53
  • This was the answer for me. The strange part is that I used the same exact images/recycler/... In another part of my app, same activity, same layout, and no problem. It's really strange – Feuby Aug 11 '20 at 22:44
4

This is because the image is taking time to load. Firstly, change your code for loading image as the below one...

Picasso.get().load(list.get(position).id).fit().centerCrop()
                .placeholder(R.drawable.ic_launcher)
                .into(holder.cardimage);

Then, before setting the adapter on recyclerview to the instance of CardAdapter inside MainActivity add this line..

        yourAdapter_name.setHasStableIds(true);

One more tip...If anyone is using large drawables (xxxhdpi, xxhdpi ), first convert them into mdpi or ldpi . This will also improve the performance a lot and also reduces the apk size.You can use this website NativeScript Image Builder

Mrudul Tora
  • 390
  • 2
  • 11
3

This could be because of Picasso taking time to load the image. Use the below snapshot and adjust accordingly.

Picasso.with (context)
                    .load (url).fit().centerCrop()
                    .error (R.drawable.UserImage)         
                    .into (imageView);

Use fit() and centerCrop() to avoid legginess.

Sukhbir
  • 1,033
  • 9
  • 28
Harpreet Singh
  • 330
  • 3
  • 5
2

You need to be getting the images asynchronously. As it is now, it stalls to actually download the image.

Leave the imageview blank when it's created, but have some sort of listener to set the image when it's been downloaded.

BooleanCheese
  • 594
  • 8
  • 28
2

if you use Recyclerview in vertical mode and your activity contains other item that you have ScrollView then you must use NestedScrollView instead of ScrollView.

as described in google documentation NestedScrollView is just like ScrollView, but it supports acting as both a nested scrolling parent and child on both new and old versions of Android. Nested scrolling is enabled by default.

MSH
  • 173
  • 2
  • 15
1

I had laggy recyclerView issue when one of the textView within the recyclerView was receiving null values. I fix this and my recyclerView stopped lagging.

S bruce
  • 1,454
  • 3
  • 14
  • 23
1

It might be late, but maybe somebody will find it useful. Had the same problem, problem was at line:

Picasso.get().load(image).into(holder.imageView); 

so I added fit().centerCrop() like this:

Picasso.get().load(image).fit().centerCrop().into(holder.imageView);

That solved my problem

0

I am bit late but this can be fixed bu using thumbnail with Glide.

     Glide.with(context)
                .load(URL)
                .dontAnimate()
                .diskCacheStrategy(DiskCacheStrategy.ALL)
                .thumbnail(0.5f)
                .centerCrop()
                .into(imageView);
0

In my case, bitmap creation was making delay for scroll. So I put bitmap creation in background thread and set to image in UI thread. This way, UI is not blocked for bitmap creation

new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Bitmap myBitmap = BitmapFactory.decodeFile(filepath);
                        activity.runOnUiThread(new Runnable() {
                            public void run() {
                                vItem.imgProfile.setImageBitmap(myBitmap);
           
                            }
                        });
                    }
                }).start();
0

from my experience with recyclerview what i found the possible cause could be is the image size. beside that adding property ( setNestedScrollingEnabled(false); ) to the recyclerview and ( setHasStableIds) to the adapter does help to make recyclerview work smoothly. So, bottom line is, if your recyclerview has image to load from network make sure to override their size or use thumbnail property(in glide). this tricked saved my life and i hope it might save others life as well. Happy coding :)

avinash
  • 236
  • 4
  • 7
0

A bit late but I found leveraging Kotlin Coroutines as an effective way to make your onBindViewHolder() calls cheap, by offloading your image loading on to a background thread. I use Glide to load 384x384 images and the scrolling is smooth for upto a couple of thousand images.

// Here, defaultScope = CoroutineScope(Dispatchers.Default + SupervisiorJob())
// Key idea is to launch the Glide call on a background thread.

defaultScope.launch(exceptionHandler) {
    Glide.with(view)
        .load(uri)
        .also {
            // When Glide is done preparing the resource, 
            // use Main thread to load into ImageView.
            withContext(Dispatchers.Main) {
                it.into(view)
            }
        }
}

For Picasso, it will be a similar thing (though I haven't tried it personally), where into() must be invoked from the Main thread and the preparation on the background thread.

Siddharth Kamaria
  • 965
  • 1
  • 7
  • 17
-3

I think the reason is that Picasso caches your images but since you have a list of drawables that you bundle together with your app, you do not in theory need to cache your images. Caching is only useful when you are downloading the image from the internet and you don't want the app to redownload the images each time you swipe up or down on the recyclerview.

I would adjust the way picasso works by changing the memorypolicy so try this instead :

 Picasso.with(getContext()).load(data.get(pos).getFeed_thumb_image()).memoryPolicy(MemoryPolicy.NO_CACHE).into(image);
Simon
  • 18,312
  • 22
  • 130
  • 197
  • Thank you, but without caching situation is even worse . I found that when using resize sitution gets better once when all images are loaded, but still lag while scrolling down first time.But setting fixes size doesn't work in my case bacause I want image to fill the card wight . – phlosopher_mk Oct 22 '15 at 20:22
  • If included drawables are in memory or not is the question. If you loading the drawables during the RecyclerView this would be causing hitching. Drawables need to be loaded into memory before the RecyclerView starts. All non-included drawable may need to be cached before the RecyclerView starts.I'm not sure if the RecyclerView (or some other part your using) is using the UI thread or BG thread. If its already using a BG thread you may need to make sure they are not competing – CmosBattery Oct 22 '15 at 23:40
  • 2
    Here is demo of project https://drive.google.com/file/d/0BypaioeS7MXPNHY3ZUFiNVBtcTg/view?usp=sharing . I am loading drawables with picasso, how can I load them before recycleView ? I tried calling initializeData() method after on create,but there is stil lag on the first scroll . Remember,this only hapens on 2k resolution. On full hd everything is smooth. – phlosopher_mk Oct 23 '15 at 02:09