I am doing an API request to Imgur using Retrofit in order to retrieve about 40 URLs and then display them in a RecyclerView using Glide like such:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Glide
.with(context)
.load(list.get(position).getLink()) // Gets the URL in my list
.centerCrop()
.placeholder(R.mipmap.placeholder)
.crossFade()
.into(holder.imageView);
}
The request gets answered quickly, but most of my images stay as placeholders as some of them appear one by one later on.
What is causing this delay in the display of my images? Is it perhaps linked to the speed of my Internet connection?
Additionally is my approach a correct one when it comes to "large" amounts of pictures?
Please note that most ImageViews do not load, even the ones that are visible to my user.
This might be depending on your internet connection. If so, you can use imgur smaller sizes, you can add one of those letter at the end of your filename:
s = Small Square (90×90)
b = Big Square (160×160)
t = Small Thumbnail (160×160)
m = Medium Thumbnail (320×320)
l = Large Thumbnail (640×640)
h = Huge Thumbnail (1024×1024)
i.e. this:
http://i.imgur.com/bF8zPs4.jpg
becomes this (for the small square):
http://i.imgur.com/bF8zPs4s.jpg
Once you've set the url for the image in Glide, the image itself still needs to be downloaded and shown in the ImageView, which is causing the delay. If the images are not loading, can you check if the image url loads the image in your browser for example?
A better approach to load images in an adapter with Glide is to use a RequestManager which you pass in the constructor of your adapter. Glide will then subscribe to the lifecycle of your activity of fragment. This will prevent images from being downloaded when your activity of fragment has been destroyed, which is especially useful when Glide has to download large images.
public MyAdapter(List<Object> items, RequestManager requestManager) {
this.items = items;
this.requestManager = requestManager;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
requestManager
.load(list.get(position).getLink()) // Gets the URL in my list
.centerCrop()
.placeholder(R.mipmap.placeholder)
.crossFade()
.into(holder.imageView);
}
And then you can call the constructor of your adapter in an acitivty or a fragment as such:
MyAdapter myAdapter = new MyAdapter(items, Glide.with(this));
I've been using this approach after I've found this answer from TWiStErRob.
it is depend on your internet connection speed and the size of images.
if the size of images it to large,it may cause of of Memory exception.
In Recyclerview, Glide only loads images in visible imageViews. ImageViews that are not visible will be loaded once you scrolldown. This is due to recycler property of RecyclerView.
Add following line in you onBindViewHolder and see the logs, you will understand it:
Log.w("Link", list.get(position).getLink());
Related
I developed a chat app. It uses RecyclerView to show chat info. It's exactly like facebook messenger.
My problem is, loading the image of user I'm chatting to takes time, and make the app slow when there's large number of chat messages.
I'm using picasso to load image.
Picasso.get()
.load("https://domain/images/profile_picture/" + otherImage)
.networkPolicy(NetworkPolicy.OFFLINE)
.transform(new CircleTransform())
.into(imageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError(Exception e) {
int placeHo;
if(otherGender.equals("female"))
placeHo = R.drawable.ic_female_color;
else
placeHo = R.drawable.ic_male_color;
Picasso.get()
.load("https://domain/images/profile_picture/" + otherImage)
.error(placeHo)
.transform(new CircleTransform())
.centerCrop(Gravity.TOP)
.into(imageView, new Callback() {
#Override
public void onSuccess() {
}
#Override
public void onError(Exception e) {
Log.v("Picasso","Could not fetch image");
}
});
}
});
Is there any efficient way to show the image? as it's showing the same image(user profile picture).
I can think only one reason that your RecyclerView is lagging. Because your image size is large.
See, if your chat image is about 50-100dp then you should use the same resolution image. Perhaps you are loading original image.
AFAIK I use Glide over Picasso because Glide optimize the downloaded image, as ImageView size.
See Glide doc
Glide's primary focus is on making scrolling any kind of a list of
images as smooth and fast as possible, but Glide is also effective for
almost any case where you need to fetch, resize, and display a remote
image.
You don't need to worry about cache in Picasso and Glide both. Cache is enabled by default in Picasso, so if same image is queried again, then it will be picked from cache.
Solution 1 (using Piccaso)
Resize image as much you need.
Picasso
.with(context)
.load(UsageExampleListViewAdapter.eatFoodyImages[0])
.resize(100, 100) // resizes the image to these dimensions (in pixel). does not respect aspect ratio
.into(imageViewResize);
Solution 2 (Using Glide)
You need not to worry about anything, you are good to go if you use Glide.
keep it Static
Or You can set it at the time of initialisation
not changing it every time on viewholder
sorry for my english and format of writing
If you are using the same image in all the Views of the same viewType then load the image in onCreateViewHolder method of the adapter and not in onBindViewHolder.
The same View with the image already loaded will be recycled (reused) and not loaded again and again, the result will be for sure faster.
You have to postpone the first image retrieve procedure at when the RecyclerView.OnChildAttachStateChangeListener() event signals that a child is added. Obliviously the Event is triggered for each of firsts visible items. The most correct way to postpone something like this is to create an Handler and then send/enqueue a Handler.sendMessage(Message.obtain(..)) one for each visible objects) items. Inside the Handler you will receive that Message and there you can execute the Picasso action.
Then you have to decide if load other (the not yet visible items) images all at once or just prepare the picture only when it is required by the Recycler.
I'm currently trying to create an image "player" to show a person's drive. The drives are recorded and stored as images, split as 1 image per second.
I'm pulling the images using Glide. However, as you can imagine, there are a lot of images - thousands of them. My first idea was to download the images using the downloadOnly option of Glide, and use a Discrete Seekbar, and as the images are getting downloaded, then increase the max of the Seekbar. The problem with the approach, is that it takes a long time.
This is what I currently have to download the images:
private void downloadOnlyImages(List<String> URLs){
totalImages = 0;
for(final String url: URLs){
Glide.with(this)
.load(url)
.downloadOnly(new SimpleTarget<File>() {
#Override
public void onResourceReady(File resource, GlideAnimation<? super File> glideAnimation) {
Log.i("glide", "image downloaded" + url);
mySeekBar.setMax(totalImages++);
}
});
}
}
and this is what I'm currently using with the seekbar:
mySeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
#Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
Glide.with(getActivity())
.load(URLs.get(progress))
.fitCenter()
.dontAnimate()
.diskCacheStrategy(DiskCacheStrategy.ALL)
.into(imageView);
}
The idea was, download the images, and then use the seekbar to move through them, always replacing the same ImageView with the next image. Using Glide's cache, I'm trying to check if it's in cache then pull that image if not then fetch using the URL. But, this hold up the UI Thread, which is causing the delay.
TLDR: I want to be able to be able to use a seekbar as a "player" for thousand of images that I'm pulling using Glide. However, it's very laggy using my naive approach. Trying to think of a more effective and efficient way to pull the images, in a more "lazy loading" way.
I am using Picasso to handle image loading and caching in my Android Udacity project and I am noticing the caching is not working as I'd expect:
As you can see on the left fragment, the image has already loaded in an earlier thread. Now, with the same URL link, I am asking Picasso to place that image in the fragment on the right.
Here is the code which generates the grid view on the left fragment (and occurs first):
https://github.com/esend7881/udacity-android-popmovie/blob/a9a1b9a19a37594bb5edd736b7ec59229fb5905a/app/src/main/java/com/ericsender/android_nanodegree/popmovie/adapters/GridViewAdapter.java#L71
String load = String.format(sImgUrl, sImgSize, movie.poster_path);
Picasso.with(mContext.getApplicationContext())
.load(load)
.placeholder(R.drawable.abc_btn_rating_star_on_mtrl_alpha)
.error(R.drawable.abc_btn_rating_star_off_mtrl_alpha)
.resize(550, 775)
.into(viewHolder.imageView);
And then here is the code which runs in the right fragment:
https://github.com/esend7881/udacity-android-popmovie/blob/a9a1b9a19a37594bb5edd736b7ec59229fb5905a/app/src/main/java/com/ericsender/android_nanodegree/popmovie/fragments/MovieDetailsFragment.java#L308
Picasso.with(getActivity().getApplicationContext())
.load(String.format(sImgUrl, sImgSize, mMovieObj.poster_path))
.error(R.drawable.blank)
.fit()// .resize(366, 516)
.into(mMovieThumb, new com.squareup.picasso.Callback() {
#Override
public void onSuccess() {
Utils.log(sw.toString());
Utils.hideViewSafe(mMovieThumbProgress);
}
#Override
public void onError() {
Utils.log(sw.toString());
Utils.hideViewSafe(mMovieThumbProgress);
}
});
I am using the same application context in each as well as the load text:
String.format(sImgUrl, sImgSize, mMovieObj.poster_path))
and
getActivity().getApplicationContext()
So, I would think Picasso ought to detect when the exact same URL load link appears in the same context within a short period of time from each other and Picasso would then load the exact same image back into the app.
If this is not how Picasso caching works, then how does it?
As a comment mentioned, I'd guess this is affected by the size of the image being different in both fragments.
I'd recommend using https://github.com/facebook/fresco instead of picasso. It's more efficient, especially with different sizes. You can also directly access cached files if required https://github.com/facebook/fresco/issues/80
It's probably related to the HTTP headers received when getting the image that do not allow caching, as Picasso relies on an HTTP component to do the caching.
Try uploading your image on imgur, try hardcoding that path and see if it works. If that's the case, you'll have to find a workaround on how to get the image from the movie database.
I have a List View of images.
If no image exists in the local cache, the placeholder image will remain in place of the image.
Then an asynchronous task is run to download the image from a server.
Once the async task finishes I set the imageView bitmap to the downloaded image.
Then I call notifyDataSetChanged()
The problem is that I need to scroll down and scroll back up for each cell to show the new image that has been downloaded. It doesn't update the cells automatically.
So I have:
public class ImageLoadTask extends AsyncTask<Void, Void, Bitmap> {
private ImageView imageView;
public ImageLoadTask(ImageView imageView) {
this.imageView = imageView;
}
#Override
protected Bitmap doInBackground(Void... params) {
// download image from server and return
}
#Override
protected void onPostExecute(Bitmap result) {
super.onPostExecute(result);
imageView.setImageBitmap(result)
notifyDataSetChanged()
}
}
and in my getView() method I have:
if(image exists in cache){
myImageView.setBitmap(image in Cache);
}else{
new ImageLoadTask(myImageView).execute()
}
So any images that were stored in the Cache work fine.
Any images that have to be downloaded first will not update correctly, unless you scroll down and back up again.
What is the problem?
I believe you are doing thigs which have been already done and are working rly fine.
I advise you to use library for loading images with listview. One of these is picasso Picasso Android library. This library provides downloading and caching, and can easily (with one line) display them to imageview. Also it provides possibility for placeholder image.
Picasso.with(context)
.load(url)
.resize(50, 50)
.centerCrop()
.into(imageView)
Here is another question on stackoveflow:
Using Picasso library with ListView
EDIT:
If you use picasso you will ALSO solve problem with images wrong displaying while scrolling. This happens if you are loading/downloading them in AsyncTask and you are not doing it correctly.
I was answering question about this problem few daays ago: Why are wrong images getting loaded in ListView occasionally?
Firstly, if you are using the ImageView object directly, then you don't need to call notifyDataSetChanged.
Secondly, what you need to ensure is that the ImageView object you are passing still exists for the same URL requested, i.e., as you must know that Android will re-use views and might be if in between you had scrolled the ImageView object for which you had requested for an URL would have changed to some other URL.
I have worked on a very similar scenario, where I had a separate design like Adapter -> ImageViewRequest(This class will be used to post request, and this class maintains a map of ImageView and URL. So for every request entry is done to the map, and for every response we check that the response for the URL is the current requested URL for the entry of that ImageView and then set accordingly) -> AsyncTask
And also ensure that while setting the bitmap you do that inside a runOnUIThread task to ensure that the image is properly loaded, otherwise you might get a crash saying you are setting in a wrong thread.
Hope that helps :)
I can't find in any really credible source explaining what LazyList is. Anyone?
Lazy List is lazy loading of images from sd-card or from server using urls. It is like on demand loading of images.
Images can be cached to a local sd-card or your phone's memory. URL is considered the key. If the key is present in the sd-card, images get displayed from sd-card, otherwise it downloads the image from the server and caches it to a location of your choice. You can set a cache limit. You can also choose your own location to cache images. Cache can also be cleared.
Instead of the user waiting to download large images and then displaying them, lazy list loads images on demand. Since images are cached, you can display images offline.
https://github.com/thest1/LazyList. Lazy List
In your getview
imageLoader.DisplayImage(imageurl, imageview);
ImageLoader Display method
public void DisplayImage(String url, ImageView imageView) //url and imageview as parameters
{
imageViews.put(imageView, url);
Bitmap bitmap=memoryCache.get(url); //get image from cache using url as key
if(bitmap!=null) //if image exists
imageView.setImageBitmap(bitmap); //display iamge
else //downlaod image and dispaly. add to cache.
{
queuePhoto(url, imageView);
imageView.setImageResource(stub_id);
}
}
An alternative to Lazy List is Universal Image Loader
https://github.com/nostra13/Android-Universal-Image-Loader. It is based on Lazy List (it works on the same principle), but it has lot of other configurations. I would prefer to use Universal Image Loader because it gives you more configuration options. It can display an error image if a download failed. It can display images with rounded corners. It can cache on disc or memory. It can compress an image.
In your custom adapter constructor
File cacheDir = StorageUtils.getOwnCacheDirectory(a, "your folder");
// Get singletone instance of ImageLoader
imageLoader = ImageLoader.getInstance();
// Create configuration for ImageLoader (all options are optional)
ImageLoaderConfiguration config = new ImageLoaderConfiguration.Builder(a)
// You can pass your own memory cache implementation
.discCache(new UnlimitedDiscCache(cacheDir)) // You can pass your own disc cache implementation
.discCacheFileNameGenerator(new HashCodeFileNameGenerator())
.enableLogging()
.build();
// Initialize ImageLoader with created configuration. Do it once.
imageLoader.init(config);
options = new DisplayImageOptions.Builder()
.showStubImage(R.drawable.stub_id)//display stub image
.cacheInMemory()
.cacheOnDisc()
.displayer(new RoundedBitmapDisplayer(20))
.build();
In your getView()
ImageView image=(ImageView)vi.findViewById(R.id.imageview);
imageLoader.displayImage(imageurl, image,options);//provide imageurl, imageview and options
You can configure Universal Image Loader with other options to suit your needs.
Along with LazyList/Universal Image Loader you can view this website for smooth scrolling and performance.
http://developer.android.com/training/improving-layouts/smooth-scrolling.html.
AFAIK, I'll explain you with the example
If you list contain lot of images with Text, it will take some time for your list to load because you need to download images and you need to populate them in the list. Suppose if your list contains 100 images It will take lot of time to download each image and to show it the listitem. To make the user wait until the images loads is not user friendly.
so What we need to do. At this point of time lazy list comes into picture. It is the idea that let the images be loaded in background and show text mean while.
Everybody know that listview recycle its views for every view. i.e if your listview contains 40 elemtns then listview won't allocate memory for 40 items instead it allocate memory for the visible items, i.e say you can see only 10 items at a time. so listview will allocate 10 items meemory.
So When ever you scroll the view, then the view will refresh. because of the you'll lose your reference to images and you need to download them agian. in order to avoid that, caching comes into picture.
This example is based on my knowledge in listview, I am not saying this is only correct. There might be wrong in the answer, if any body find feel free to inform me.
I think this is the other way around. AFAIK, Lazy Loading is the definition, where you actually load the data only when you need it, and it's a good design practice.
So I believe the same applies for this, only this time it's being referring to the List View.
If I'm wrong, please correct me.
The best example of lazy list is facebook notifications,messages,requests. when you scroll then data will be load.