Glide: Glide with StaggeredGridLayoutManager

Created on 4 Oct 2016  ·  22Comments  ·  Source: bumptech/glide

Hi Glide,
I got some problems when i use Glide with RecyclerView with StaggeredGridLayoutManager.
Then i wrote a demo only for Glide to load images in RecyclerView withStaggeredGridLayoutManager.

First , let me show you the screenshot.
It is a gif ,i can't put it to here,so here is the link :http://note.youdao.com/noteshare?id=0e8fbddc4576e9236d897303da1957fd.

The problem is when i load more images to RecyclerView , the images in item will be very big.

Here is my adapter:

@Override
    public TestHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        return new TestHolder(LayoutInflater.from(mContext).inflate(R.layout.item,parent,false));
    }

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

        ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams();
        layoutParams.height = (int) ((position + 1) / 5.0 * 500);
        holder.itemView.setLayoutParams(layoutParams);

        Glide.with(mContext).load(urls.get(position)).diskCacheStrategy(DiskCacheStrategy.ALL).placeholder(R.color.colorAccent).into(holder.imageView);
    }

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

And here is when i click the "Add-Images" button:

        urls.add("http://seopic.699pic.com/photo/50004/2199.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/50000/2811.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/50007/7034.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/50006/0945.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/00040/2066.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/00010/8940.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/00041/5575.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/50007/1912.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/50004/2199.jpg_wh1200.jpg");
        urls.add("http://seopic.699pic.com/photo/50000/2811.jpg_wh1200.jpg");
        testAdapter.notifyDataSetChanged();

It is just a simple demo,but when i click the "Add-Images" button,the RecyclerView will add images and then the images in item will be very very big.
I don't know where is wrong in my code,or it is real a bug?

Sorry , my English is not good, if you don't understand ,please tell me.
Thank you very much!

question reproducible stale

Most helpful comment

When i add '.dontTransform()' , the problem is solved!!!!!!
I think maybe the default Transformer has some problem?
Now the right code is :

Glide.with(context).load(url).placeholder(R.color.gray).dontTransform().into(imageView);

I hope it can help others people.

All 22 comments

I think you meant:

layoutParams.height = (int) ((position + 1) % 5.0 * 500);

otherwise with your original code the 10th item will be ~1000px tall and the 20th ~2000px tall.

I think you meant: layoutParams.height = (int) ((position + 1) % 5.0 * 500);

No ,it is (position + 1) / 5.0 * 500. Because it is just a demo for Glide and RecyclerView.
And in fact ,the 10th item will be 1000px and the 20th 2000px tall.
It is not the problem i got.

I mean the 10th item is 1000px tall ,but when i add more 10 images,the image in 10th item will be very very big,even if the 10th item is still 1000px.

And can u see the screenshot? I think it can descripte my problem better.
And thank you very much. :smile:

If u can see the screenshot,notice the image in last item.
When i click the ADDDATA button in the top-right ,the recyclerview will add more 10 items.And in this time,the image in last item will change to very very big.

Here is another link for the same screenshot :
https://github.com/whichname/WZMRecyclerView/blob/master/GIF.gif

Hi Glide,
This is the RecyclerView with StaggeredGridLayoutManager:
image

And when i click the button , it will add more 10 items,and the image in 10th item will be very big,just like this:
image
You can see the image in 9th item is also change to very big.

And the images in 11th and 12th item is also very big.
image

I dont know where is wrong.
Can you write a simple demo to test it ?
Thank you very much!!!!

Try adding holder.itemView.layout(0,0,0,0) hack right before Glide....
If that doesn't work, take a look at the full working example at https://github.com/TWiStErRob/glide-support/tree/master/src/glide3/java/com/bumptech/glide/supportapp/github/_864_staggered_grid

I'm starting to think it might just be worth writing a custom StaggeredGridLayoutManager that preserves height at this point. the [code]holder.itemView.layout(0,0,0,0)[/code] hack prevents 'view jumping' while scrolling, but as soon as you navigate away from the list (read:change fragments) and come back to it in the backstack, the list jumps because it's beginning at a position other than the top.

Has anyone solved this completely?

@garretyoder what you described sounds like the non-staggered grid layout manager in the lib, because if you reuse a height-fixed item there's no point in staggering much. Did you try the example in glide-support?

I was talking about saving the dynamic height of every item so you know it
as soon as the view is loaded.

I don't know the height beforehand until the media is loaded, so the
example wouldn't work in my case, unless I saved the height of everything once it's loaded for the first time.

On Oct 28, 2016 1:27 PM, "Róbert Papp" [email protected] wrote:

@garretyoder https://github.com/garretyoder what you described sounds
like the non-staggered grid layout manager in the lib, because if you reuse
a height-fixed item there's no point in staggering much. Did you try the
example in glide-support?


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
https://github.com/bumptech/glide/issues/1497#issuecomment-256979456,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AC3DalQtcvGj_WBdbeFfABj1dLfgE1f7ks5q4jB6gaJpZM4KNb-_
.

Ah, I see, so cache the height of each position and assume it won't change. That might work, but keep in mind that Glide reads getWidth() first, which will be the previous position's size, even if you set the layout params to current position's size. layout(0 helps to forget the size and forces Glide to read the params or wait for layout. But you're right if you know that heights won't change, you can cache them and help the layout manager with scroll position calculations.

@TWiStErRob I managed to get it 'working' using a combination of your sample project and other related solutions from googling. My only problem, is after five or six images are loaded, Glide eventually runs out of memory and it hangs and spams the console. I've no idea what it's doing that it's using up that much memory. I'll include the layout file for the adapter item, and the bind code for the related viewholder, maybe there's something I missed.

...
if (Media.getAlbums().get(albumIndex).getMedia().get(position).getAspectRatio()!=-1) {
                fixlayout(Media.getAlbums().get(albumIndex).getMedia().get(position).getAspectRatio());
            }
            try {
                Glide
                        .with(context)
                        .load(Media.getAlbums().get(albumIndex).getMedia().get(position).getPath())
                        .asBitmap()
                        .into(new SimpleTarget<Bitmap>() {
                            @Override
                            public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
                                if (Media.getAlbums().get(albumIndex).getMedia().get(position).getAspectRatio()==-1) {
                                    Media.getAlbums().get(albumIndex).getMedia().get(position).setAspectRatio((float)resource.getWidth() / (float)resource.getHeight());
                                }
                                mediaItem.setImageBitmap(resource);
                            }
                        });
...
private void fixlayout(float aspect) {
            PercentFrameLayout.LayoutParams layoutParams = (PercentFrameLayout.LayoutParams)mediaItem.getLayoutParams();
            PercentLayoutHelper.PercentLayoutInfo info = layoutParams.getPercentLayoutInfo();
            info.aspectRatio = aspect;
            layoutParams.height = 0;
            mediaItem.setLayoutParams(layoutParams);
        }
<android.support.percent.PercentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="2dp">

    <ImageView
        android:scaleType="fitXY"
        android:id="@+id/media_item"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:foreground="?attr/selectableItemBackground"/>

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/ic_check_white_48px"
        android:padding="16dp"
        android:background="@color/selector"
        android:clickable="false"
        android:visibility="gone"
        android:id="@+id/media_item_check"/>

</android.support.percent.PercentFrameLayout>

@garretyoder Quite possibly Glide is unable to determine the size of the view and loads a screen-sized or original image. Employ a LoggingListener to see all the sizes that play in the load.

@TWiStErRob I used a simple RequestListener and dumped out the sizes, it doesn't seem like it's using more than one size per image, it's just using the original size. They are large (2k-4k) images. Glide gets through 5-6 and then just hangs, gets two or more done very slowly, then crashes.

This is for my Gallery. I'd love to know how Motorola pulled a staggered photo grid off so perfectly in their gallery.

So, there you have it. Glide cannot determine the size of the target to load into. Without that it cannot optimize Bitmaps to use minimal memory. See ViewTarget.SizeDeterminer for example.

Just noticed the codes in your prev comment: check the constructor of SimpleTarget for a clue. You're forcing Glide to load full-sized images.

@TWiStErRob That's useful to know, I didn't actually realize that was forcing it to use full sized images. Using glide's regular .into directly on the image view (Which obviously will disable caching of the image sizes) it still only gets 10-12 images in before it runs out of memory and crashes, I assume this is because of Glide's lack of support for wrap_content.

How would you go about this? All of this is for a gallery, so I don't know any image bounds until glide loads it and gives them to me. Obviously a explicit height SimpleTarget wouldn't work here because I only know the width of the image view, the height has to be dynamic.

Somehow, the Motorola gallery manages to pull off exactly what I'm after, I just wish I knew how.

_EDIT_: As a addition, I tried just using the SimpleTarget to limit the bitmap to 250,250. This fixes the memory errors, and to my surprise glide keeps the aspect ratio so it does actually 'work'. It still reshuffles the order of items when you scroll up, which I had hoped caching the height of items would prevent.

Does #781 help? If you have local images (Files) or remote with SOURCE cache you can gather info about your media and then calculate aspect based on that.

Knowing the width is good, you can try .override(w, SIZE_ORIGINAL).fitCenter().into(imageView).

If you're running out of memory is likely because your targets are not cleared. Automatic recycling only works if you use a ViewTarget (implicitly created by into(ImageView)). For non-ViewTarget you can do something like Glide.clear(holder.currentTarget); holder.currentTarget = Glide....into();

You can call setAspectRatio in a listener, you don't need a target for that. Or use into(new GlideDrawableImageViewTarget(imageView) {... onResourceReady }).

I'm sorry for reply after so many days.Because i was so busy the other day.
To add holder.itemView.layout(0,0,0,0) is not work for me.
I wrote a demo a few days ago,maybe it can descripte my problem better.
Can u check this demo?
Thank u very much.It is so kind of u.
demo:https://github.com/whichname/GlideDemo

When i add '.dontTransform()' , the problem is solved!!!!!!
I think maybe the default Transformer has some problem?
Now the right code is :

Glide.with(context).load(url).placeholder(R.color.gray).dontTransform().into(imageView);

I hope it can help others people.

Thank u so much~~~

Interesting, note that it is almost there same as using SIZE_ORIGINAL, except Glide uses inSampleSize to downsample a little when possible.

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:adjustViewBounds="true">

+

Glide.with(context).load(url).placeholder(R.color.gray).dontTransform().into(imageView);

TKS!

This issue has been automatically marked as stale because it has not had activity in the last seven days. It will be closed if no further activity occurs within the next seven days. Thank you for your contributions.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Tryking picture Tryking  ·  3Comments

r4m1n picture r4m1n  ·  3Comments

billy2271 picture billy2271  ·  3Comments

Anton111111 picture Anton111111  ·  3Comments

kooeasy picture kooeasy  ·  3Comments