So I have a RecyclerView with a custom adapter and I use Glide to download images from a URL when applicable. Relevant code snippet here:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
final Location location = mLocations.get(position);
holder.title.setText(location.getTitle());
if (location.hasImage()) {
String imageUrl = NetworkHelper.getImageUrl(getContext(),location.getImageName());
holder.image.setVisibility(View.VISIBLE);
// Use Glide for image loading
Glide.with(getContext())
.load(imageUrl)
.diskCacheStrategy(DiskCacheStrategy.ALL)
.centerCrop()
.into(new GlideDrawableImageViewTarget(holder.image) {
#Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
super.onLoadFailed(e, errorDrawable);
e.printStackTrace();
}
#Override
public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) {
super.onResourceReady(resource, animation);
}
});
} else {
// Due to the nature of the RecyclerView, we need to ensure we clear
// out images to odd behavior.
Glide.clear(holder.image);
holder.image.setVisibility(View.GONE);
}
}
Problem is that the onResourceReady() method isnt always being called and hence not all images being loaded. Ive verified all the URL's being passed and they all seem fine. Is there a Glide specific issue here perhaps? If I take a URL that is not being loaded and hard code it into the imageUrl variable, its still not being downloaded, so there must be something specific to those images thats causing glide to fail. A timeout issue perhaps?
EDIT: I added the onLoadFailed method and it does hit, but the Exception is null, which is kinda weird. Basically same issue as described here: https://github.com/bumptech/glide/issues/741
Related
In my application I'm trying to load image from an https URL using Picasso library.I am unable to load image into ImageView. I am using Picasso 2.5.2 and I am setting the image as follows:
Picasso.with(mContext)
.load(GlobalVariable.movieDetails.get(0).getVideo_cover_image())
.placeholder(R.drawable.animation_placeholder)
.error(R.drawable.animation_placeholder)
.into(iv_image);
Try this
if (TextUtils.isEmpty(imageUrl) && !isValidUrl(imageUrl)) {
iv_image.setImageResource(R.drawable.default);
} else {
Picasso.with(context)
.load(imageUrl)
.resize(70, 70)//if you want resize image
.centerInside()
.into(iv_image, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError() {
iv_image.setImageResource(R.drawable.default);
}
});
}
validate URL
public static boolean isValidUrl(String string) {
boolean b = Patterns.WEB_URL.matcher(string).matches();
return b;
}
Picasso provide a listener for image loading.
Use below code and check image loading status either Success or Error.
Picasso.with(mContext)
.load(GlobalVariable.movieDetails.get(0).getVideo_cover_image())
.placeholder(R.drawable.animation_placeholder)
.error(R.drawable.animation_placeholder)
.into(iv_imagenew,
new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
//Image Loaded
}
#Override
public void onError() {
//Error while Loading
}
});
please use resize(horizontalSize, verticalSize) method of picaso to resize image. may be your image size is to large thats why it will not load so , please try resize(horizontalSize, verticalSize)
Picasso.with(context)
.load(GlobalVariable.movieDetails.get(0).getVideo_cover_image())
.placeholder(R.drawable.animation_placeholder)
.error(R.drawable.animation_placeholder)
.into(iv_image);
.resize(600, 200) // resizes the image to these dimensions (in pixel). does not respect aspect ratio
.into(imageViewResize);
I am using Picasso library for image download and displaying it in imageView. This library also store images in cache and memory. When my internet is turn on, i am able to view images on imageView. So i think, it should also be store in cache or file memory. Now my internet is turnOFF, but it doest not display to images. kindly have a look.
Picasso.with(context)
.load(url) .placeholder(R.drawable.defaultimg)
.networkPolicy(NetworkPolicy.OFFLINE)
.into(holder.imageview2, new ImageLoadedCallback(holder.loadingBar) {
#Override
public void onSuccess() {
if (holder.loadingBar != null) {
holder.loadingBar.setVisibility(View.GONE);
}
}
#Override
public void onError(){
holder.loadingBar.setVisibility(View.VISIBLE);
Picasso.with(context)
.load(url) .placeholder(R.drawable.defaultimg)
.into(holder.imageview2, new ImageLoadedCallback(holder.loadingBar) {
#Override
public void onSuccess() {
if (holder.loadingBar != null) {
holder.loadingBar.setVisibility(View.GONE);
}
}
#Override
public void onError() {
if (holder.loadingBar != null) {
holder.loadingBar.setVisibility(View.GONE);
}
}
});
}
});
Finally I resolved that issue. Thanks #dev.bmax
image url was not correct. There is bug in Picasso. If we have url like as
https://i.ytimg.com/vi/DMVEcfQmPOs/maxresdefault.jpg?500|700
Picasso is able to displaying image when internet TURN ON
but if we TURN OFF to internet, it does not decode the url. and not display the image also.
We have to remove ?500|700 then i was able to view image in OFFLine mode also.
//url.substring(0,url.indexOf("?"))
https://i.ytimg.com/vi/DMVEcfQmPOs/maxresdefault.jpg
Thanks!
I'm using Glide to load image into a ViewPager using an PagerAdapter.
When I load the images using the following method:
Glide.with(mContext).load(mImage).placeholder(R.drawable.placeholder).into(mImageView);
Everything works fine, but now I need to get the bitmap from glide and store it in a map when it loads for future edit, so I switched this method to the following one:
Glide.with(mContext).load(mImage).asBitmap().placeholder(R.drawable.placeholder).into(new SimpleTarget<Bitmap>() {
#Override
public void onLoadStarted(Drawable placeholder) {
super.onLoadStarted(placeholder);
mImageView.setImageDrawable(placeholder);
}
#Override
public void onResourceReady(Bitmap bitmap, GlideAnimation<? super Bitmap> glideAnimation) {
if (bitmap != null) {
mImageView.setImageBitmap(bitmap);
}
mBitmapMap.put(position, bitmap);
mInterface.onImageLoaded(position, bitmap);
}
});
But the result is that the image not always shown. I think it's some how related to the fact the glide loads images async and at some point it returns when instatiateItem method already finished running.
It looks like this question is related. But the suggestions there did not help me. Has someone encountered this problem and has a solution for it?
The solution for this issue was to use another kind of target, instead of using the SimpleTarget object which I was using when I wrote the question I replaced it with the BitmapImageViewTarget object which I guess handles images asynchronously better. So the final code that I used for it is:
Glide.with(BaseApplication.getInstance()).load(newContent).asBitmap().placeholder(R.drawable.ic_action_picture).into(new BitmapImageViewTarget(mIvContent) {
#Override
public void onLoadStarted(Drawable placeholder) {
super.onLoadStarted(placeholder);
mIvContent.setImageDrawable(placeholder);
}
#Override
public void onResourceReady(Bitmap resource, GlideAnimation<? super Bitmap> glideAnimation) {
super.onResourceReady(resource, glideAnimation);
mBitmapMap.put(position, resource);
progressBar.setVisibility(View.INVISIBLE);
mIvContent.setImageBitmap(resource);
}
#Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
super.onLoadFailed(e, errorDrawable);
progressBar.setVisibility(View.INVISIBLE);
}
});
I am using Glide to download and cache images on Android. Everything works well except the fact that I don't want to load the bitmaps directly into the ImageView, I don't want to have the fade animation, neither image placeholders.
All I want is to create a global method which will help me to download the image all over the application.
public class MyApp extends Application {
public static void downloadImage(String url, final OnImageLoadedCallback callback) {
// And how to implement the listener ?
RequestListener<String, Bitmap> requestListener = new RequestListener<String, Bitmap() {
#Override
public boolean onException(Exception exc, String string, Target<Bitmap> target, boolean isFirstResource) {
callback.onDone(null);
return false;
}
#Override
public boolean onResourceReady(Bitmap bitmap, String string, Target<Bitmap> target, boolean isFromMemoryCache, boolean isFirstResource) {
callback.onDone(bitmap);
return false;
}
};
Glide.with(context)
.load(url)
.asBitmap()
.dontAnimate()
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.listener(requestListener);
}
}
The problem is that I don't know how to implement the listener. RequestListener isn't called at all.
Loads in Glide don't start until you make a call to into. The RequestListener interface observes requests, but isn't typically meant for handling results. Instead of using RequestListener, consider having your callback implement the Target interface and pass it in using into.
Alternatively you could just extend SimpleTarget and pass it in to each request in the same way you're trying to use RequestListener:
Target target = Glide.with(context)
...
.into(new SimpleTarget<Bitmap>(width, height) {
#Override
public void onResourceReady(Bitmap resource, GlideAnimation glideAnimation) {
callback.onDone(resource);
}
#Override
public void onLoadFailed(Exception e, Drawable errorDrawable) {
callback.onDone(null);
}
});
// At some point later, if you want to cancel the load:
Glide.clear(target);
You will want to provide a width and a height so that Glide can downsample and transform images appropriately. You may also run in to issues with cancellation if you're displaying these Bitmaps in views, in which case I'd strongly recommend making the view available to your loading API and passing the view to into which will handle sizing and cancellation for you.
I use Glide 3.7.0 and download images this way:
it is important to note - it executes asyncroniously
Glide.with(this)
.load(url)
.downloadOnly(new SimpleTarget<File>() {
#Override
public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {
LOGGER.debug("Photo downloaded");
}
});
When I need to show cached image, I use the same url and DiskCacheStrategy.SOURCE:
Glide.with(this)
.load(url)
.diskCacheStrategy(DiskCacheStrategy.SOURCE)
.into(imageView);
It is now even simpler on Glide 4.9.0.
Glide.with(this)
.downloadOnly()
.load(url)
From the UX point of view, it will be great to show the user a thumbnail first until the real image completes loading, then showing it to him, but Picasso uses only a resource file as the place holder like:
Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.into(imageView);
So, how can I use a thumbnail URL as the placeholder? , and if I should use Picasso twice, then how?
An issue is already opened on Picasso's github page with this request, but seems it won't be added to Picasso as per JakeWharton. So how could we do it with what's available in hand?
Thanks to raveN here & the comments on the original request on github, finally I've got a working solution:
Picasso.with(context)
.load(thumb) // thumbnail url goes here
.into(imageView, new Callback() {
#Override
public void onSuccess() {
Picasso.with(context)
.load(url) // image url goes here
.placeholder(imageView.getDrawable())
.into(imageView);
}
#Override
public void onError() {
}
});
The trick here is to get the drawable from the imageView (which is the thumbnail) after the first call & pass it as a placeholder to the second call
-- update --
I've made a blog post describing the whole scenario
You could write a simple helper which calls Picasso twice (as you mentioned).
I've not tested it, but it should go like
Picasso.with(context)
.load(thumbnailUrl)
.error(errorPlaceholderId)
.into(imageView, new Callback() {
#Override
public void onSuccess() {
// TODO Call Picasso once again here
}
#Override
public void onError() {
}
);
There are a couple of different ways to get your Picasso called twice. One method I could think of (again, not tested) is
public static void loadImageWithCallback(String url, Callback callback) {
Picasso.with(context)
.load(url)
.error(errorPlaceholderId)
.into(imageView, callback);
}
public static void loadImage(String url) {
Picasso.with(context)
.load(url)
.error(errorPlaceholderId)
.into(imageView);
}
loadImageWithCallback("http://example.com/mythumbnail.jpg", new Callback() {
#Override
public void onSuccess() {
loadImage("http://example.com/myRealImage.jpg");
}
#Override
public void onError() {
}
}
Edit: All I know is that Picasso provides this callback mechanism. I'm using it in my app to hide a ProgressBar that is displayed until the image is loaded. I'll hide it in success or error callbacks - so you'll have the option to get notified when image loading is done. Then you can simply call it again. I hope the above approach works.
I originally used AbdelHady's solution but found that the larger image is only loaded after the thumbnail is done loading so I came up with this instead.
Assuming you have a utility class in your project;
public final class U {
public static void picassoCombo(final RequestCreator thumbnail,
final RequestCreator large,
final ImageView imageView) {
Target target = new Target() {
#Override
public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
imageView.setImageBitmap(bitmap);
}
#Override
public void onBitmapFailed(Drawable errorDrawable) {
imageView.setImageDrawable(errorDrawable);
}
#Override
public void onPrepareLoad(Drawable placeHolderDrawable) {
thumbnail.into(imageView);
}
};
imageView.setTag(target); // To prevent target from being garbage collected
large.into(target);
}
}
Usage:
U.picassoCombo(
Picasso.with(context)
.load("http://lorempixel.com/200/100/sports/1/")
.placeholder(R.drawable.ic_image_placeholder),
Picasso.with(context)
.load("http://lorempixel.com/800/400/sports/1/")
.error(R.drawable.ic_image_broken),
imageView
);
In the above example the placeholder is set first, the thumbnail url is set next, and regardless of whether the thumbnail request is done, successful, or failed, the large image request is set once it is done. If the large image request failed, then the error drawable is set.
The only issue is that if you use setIndicatorsEnabled(true) the debug indicators don't show for the large request. As far as I can tell this seems to be by design according to this issue convo