I'm using glide to load an image into the background of one of my activities. This background image changes very often and I use the crossfade() to animate between the image changes. This works all fine and dandy when the image is not in the cache.. the crossfade works as expected. However, it seems that if the image was already in the memory cache, the animation does not happen and the image just instantly loads.
Is it currently possible to always animate regardless if its in the cache or not? If not, can this be added? The instant loading really messes with the fluidity of my app
You can override Glide's default behavior using the RequestListener interface.
Return true from onResourceReady to handle the result yourself, then cast the target to an ImageViewTarget or whatever Target class you're using, set the result, and start whatever animation you want.
It's not elegant, but it works. My general thought was that usually people don't want to see animations on images loaded from cache.
Thanks. Using glide for lists.. yes people dont want to see the animations.. but there are plenty of other use cases for wanting to see animations.
The request listener should be fine for me.. thanks for the fast response
Thanks for the feedback, I'll consider how we might be able to make this easier in the future, suggestions always welcome!
So.. while trying to implement this i ran into an interesting issue
Here is the code i used in the listener
@Override
public boolean onResourceReady(GlideDrawable resource, AlbumArtWrapper model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource)
{
ImageViewTarget<GlideDrawable> image_target = ImageViewTarget<GlideDrawable>)target;
Drawable current = image_target.getCurrentDrawable();
if (current != null)
{
TransitionDrawable transitionDrawable = new TransitionDrawable(new Drawable[]{current, resource});
transitionDrawable.setCrossFadeEnabled(true);
transitionDrawable.startTransition(1000);
image_target.setDrawable(transitionDrawable);
return true;
}
else
return false;
}
When running this.. visually it would appear that the same image would crossfade into itself. I put a breakpoint right after "current" was set and compared it to "resource". The drawables were the same according to the memory reference location. I ran it a bunch more times in the debugger and sometimes the references would be different.. but the majority of the time, the drawable in the target was already set to the resource being passed into the method. Now keep in mind this only happened when the resource was loaded from the memory cache. When it was loaded from disk it behaved like expected
I tried to trace through the code to find out how this was happening but had no luck. Anyway i did find a workaround that i can use.. but i just wanted to make it aware there seems to be a potential race condition (or just something setting the target's resource before this listener is called
The behavior you describe is, at least currently, expected. If you're showing an image in a view and you then attempt to load the same image into the same view, the only reason you don't see the image disappear and re-appear is that we get a cache hit so the second load completes synchronously.
The 2.x version of Glide made an attempt to avoid restarting requests like this when we knew we were loading the same image, but doing so requires not only that the image you're requesting is identical to the one already present, but also that all of the options you used to construct the load (placeholders, transformations, etc) are identical those used previously. Comparing all of the options requires each option to implement equals and hashcode which we can't guarantee for all argument types (Drawables for example).
Rather than restrict the api to only allowing options we knew could be compared, the 3.x version of Glide simply relies on existing images to be in cache, but otherwise treats every new load equally, even if the new load is for an image that is already being displayed.
The side affect of that choice is that you may get an onResourceReady call in your RequestListener or Target with a resource your Target is already displaying.
Thanks for the write up, it's definitely a bit of a corner case.
I'm not trying to load the same image into the view. I load image 1 -> crossfade to image 2.. works as intended. I then crossfade back to image 1 and what i described above happens. Image 1 was still in the memory cache.
My app is a music player and im basically just crossfading between the album art images of whatever song is playing. Users regularly will go back to the previous song which i would like the image to crossfade back into the previous image
Ah I misunderstood you. Are you using a thumbnail request to get the cross fading to work? Can you paste your Glide.with() line?
Normally loading a new image will set a placeholder drawable (or clear out any existing image if you're not using a placeholder) which would make crossfading between two images impossible. You can get around it a couple of ways, which may be related to the problem you describe.
I actually get the drawable from the imageview before making the glade call so I ended up just using that instead of the drawable returned from the target in the crossfade/TransitionDrawable. Not ideal but it works
Drawable placeholder = background.getDrawable();
background.setAlpha(target_alpha / 255.0f);
Glide.with(m_Context)
.load(album_art_wrapper)
.placeholder(placeholder)
.transform(new StackBlurTransform(m_Context))
.listener(new CrossfadeRequestListener<AlbumArtWrapper>(placeholder))
.into(background);
The crossfaderequestlistener does the same as the previous code but uses placeholder instead of current in the TransitionDrawable
With the help of @TWiStErRob, we've reproduced this here: https://github.com/TWiStErRob/random/tree/master/GlideSongs. Working on looking in to this more.
I've thought about this a bit more. Here's the basic lifecycle of a Glide request.
1, 2, and 4 all prevent a simple cross fade from one loaded image to another.
I can solve point 4 by allowing some option on a load that lets you determine whether or not an animation fires when an image is loaded from the memory cache. However, I can't solve 1 and 2 in the context of a single request.
Because we want to cross fade between two images, and each request clears out the resources of the previous request, we cannot use a single Target. Although @gonemad's solutions mostly works here, it's actually unsafe, The image you're animating from has been returned to the bitmap pool and/or recycled and may throw an exception or cause artifacts when drawn. As long as there aren't a lot of concurrent requests and you're on honeycomb and up, the window in which these errors can occur is small and you're not likely to see them, which is why this mostly works.
The correct solution is to use two custom ViewTarget's. Load the first image into Target A, then the second image into Target B. When the load into Target B completes, get the image from Target A and crossfade from A to B. Each subsequent load would swap the two Targets. You would need to override the getRequest() method in each Target to hold the request as an instance variable instead of using the View's setTag() and getTag() methods to keep the requests independent.
We can probably write a custom class that would contain most or all of the logic I describe and provide it as a utility class.
any update on providing this as an utility class? You were right about it being unsafe.. i do have a few error reports from GlideBitmapDrawable.draw where it throws an exception trying to draw a recycled bitmap and im assuming this is the reasoning
Yeah that sounds like what I'd expect... Unfortunately no, I haven't had time to do this yet. If you have the chance to give it a try, I'd love to see a pull request. We basically just need a class to wrap two targets and make it easy to swap between them (plus the caveats about get/setRequest in my earlier comment).
Hello, I'm looking for the same thing, just to force the crossfade when image are already cached (Usefull to loop between few images). Any news about this ? Thank you !
The crossfade works perfect when It's loaded from the server, can you just make an async request from cache ? This is my code:
private Runnable mRunnable = new Runnable() {
@Override public void run() {
if (timer_run) {
timer_run=false;
if (isVisible())
Glide.with(frag)
.load(getString(R.string.url_res) + getString(R.string.path_img_home_mz) + i_img + getString(R.string.file_ext_jpg))
.placeholder(image.getDrawable())
.listener(new RequestListener<String, GlideDrawable>() {
@Override public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) {
timer_run = false;
return false;
}
@Override public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) {
timer_run = true;
i_img++;
if (i_img > max_img) {
i_img = 1;
}
hTimer.postDelayed(mRunnable, RestoVal.HOMES_TRANSITION_TIMER_MS);
return false;
}
})
.crossFade(RestoVal.IMAGE_FADE_DURATION)
.into(image);
}
}
};
Yeah I'd also like to know if someone has a solution for this as of now.
any news about this ?
@neoteknic for your use case:
just to force the crossfade when image are already cached (Usefull to loop between few images).
take a look at https://github.com/bumptech/glide/issues/527#issuecomment-148840717.
@sjudd @TWiStErRob
I load thumbnails using .thumbnail(pathToThumbFile) along with my load line.
I'm facing this issue that the cross fade always happen no matter whether the image is cached or not. When I scroll to already cached images I see the cached image first then I see the thumbnail cross fading to the image again.
requestManager.load(mediaPath).clone().centerCrop().override(requestedSize[0], requestedSize[1])
.thumbnail(requestManager.load(thumbPath).clone().fitCenter().override(requestedSize[0], requestedSize[1]))
.crossFade()
.into(((AlbumGridViewHolder) viewHolder).imageView)
@TWiStErRob
The answer you have posted doesn't have an new DrawableCrossFadeFactory
Also I have a ScaleIn Animation that I want to show no matter it in cache or the Image is loaded for first time.
can you tell me any work around
@PrathameshKesarkar it does have a new DrawableCrossFadeFactory that's the core of the linked solution. Can you please open a new issue and describe what you want in more detail, also share what you already have.
Hi I just migrated from v3 to v4. And wondering if any solution for this now?
OK. I made a small library ViewTransitioner can be used along with Glide for transition purpose. This project is just for me to use in my the other project. But if someone wants to use transition for all resources all the time can check out the example.
https://github.com/zeroarst/ViewTransitioner
I hope this feature will come soon 馃憤
Any updates on this?
This is possible in v4, though you have to implement your own TransitionFactory: http://bumptech.github.io/glide/doc/transitions.html#custom-transitions.
I am having a problem with this.
I go to Activity B from Activity A. It crossfade from a ColorDrawable (Placeholder) to a Image from an URL. I go back to Activity A, then I go to Activity B, the crossfade is not working.
I tried to use DrawableCrossFadeFactory but I am getting the same issue.
@sjudd is there a way to fix this problem?
I think should be great a method to force crossfade always.
@JavierSegoviaCordoba see http://bumptech.github.io/glide/doc/transitions.html#custom-transitions. You can choose to cross fade always, even when the request was loaded from memory.
Most helpful comment
This is possible in v4, though you have to implement your own
TransitionFactory: http://bumptech.github.io/glide/doc/transitions.html#custom-transitions.