RecyclerView of RecyclerViews: Image Loading - android

I try to implement a layout that contains a vertical RecyclerView that itself contains multiple horizontal RecyclerViews (imagine a news site, with multiple news sections, where each section contains multiple news stories that can be scrolled horizontally).
The horizontal RecyclerView contains (among other things) an ImageView. I'm looking for the optimal strategy of loading an image from an URL into this ImageView.
Firstly, my code. The Adapter for the outer RecyclerView:
#Override
public ItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.content_preview_list_item, null);
ItemRowHolder mh = new ItemRowHolder(v);
mh.recycler_view_list.setAdapter(new SectionListDataAdapter(mContext, listener, new ArrayList<ContentPreviewButtonItem>()));
mh.recycler_view_list.setLayoutManager(new LinearLayoutManager(mContext, LinearLayoutManager.HORIZONTAL, false));
return mh;
}
#Override
public void onBindViewHolder(ItemRowHolder itemRowHolder, int i) {
Log.d("RecyclerViewDataAdapter", "onBindViewHolder");
final String sectionName = dataList.get(i).getTitle();
ArrayList singleSectionItems = dataList.get(i).getItems();
itemRowHolder.itemTitle.setText(sectionName);
((SectionListDataAdapter)itemRowHolder.recycler_view_list.getAdapter()).setItemList(singleSectionItems);
itemRowHolder.recycler_view_list.getAdapter().notifyDataSetChanged();
itemRowHolder.recycler_view_list.setHasFixedSize(true);
}
And for the inner RecyclerView that contains the images:
#Override
public SingleItemRowHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
Log.d("SectionListDataAdapter", "onCreateViewHolder");
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.content_preview_item, null);
SingleItemRowHolder mh = new SingleItemRowHolder(v);
return mh;
}
#Override
public void onBindViewHolder(SingleItemRowHolder holder, int i) {
Log.d("SectionListDataAdapter", "onBindViewHolder");
ContentPreviewButtonItem singleItem = itemsList.get(i);
Uri imageUri = Uri.parse(singleItem.getImageUrl());
holder.itemImage.setImageURI(imageUri); //itemImage is a SimpleDraweeView
}
While this code works great on mobile, there's a noticeable lag on tablet when multiple inner RecyclerViews and images are loaded at once. Right now I use Facebook's Fresco library to load the image into a SimpleDraweeView. I also tried Picasso but that yield an OutOfMemoryException when multiple images had to be loaded at once.
I improved the performance by moving the adapter creation of the inner RecyclerViews into the onCreateViewHolder instead of the onBindViewHolder, but the lag is still noticeable.
What's the best strategy or library to load images in this scenario.
Thanks

Glide is a perfect fit for this I believe using which you can resize the image also while the image is loading you can display a thumbnail of the same

I am doing the same in my project and so far the best service I got is from Picasso library. Their resizing capabilities are the best. Might be a little slow on first load but subsequent loading is super fast.

Related

Android RecyclerView lags when scrolling

I have the following code in my recycler adapter. When scrolling down, recycler starts to lag while glide loads asynchronous images. If I remove glide then is all perfect. Any ideas?
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
Customer customer= customerList.get(position);
((CustomerViewHolder)holder).binding.setCustomer (customer);
RequestOptions ro = new RequestOptions();
ro.diskCacheStrategy(DiskCacheStrategy.NONE);
ro.skipMemoryCache(true);
ro.centerCrop();
Glide.with(mCtx)
.applyDefaultRequestOptions(ro)
.load(customer.getImagePath())
.into(((CustomerViewHolder)holder).binding.imageView);
((CustomerViewHolder)holder).binding.executePendingBindings();
}
Be sure to set a fixed height for your ViewHolder prior to loading the image. If the ViewHolder is resized as it's binding, it will cause the scroll to stutter.

RecyclerView adapter showing wrong images

I have a RecyclerView adapter that looks like this:
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
private static Context context;
private List<Message> mDataset;
public RecyclerAdapter(Context context, List<Message> myDataset) {
this.context = context;
this.mDataset = myDataset;
}
public static class ViewHolder extends RecyclerView.ViewHolder implements View.OnCreateContextMenuListener, View.OnClickListener {
public TextView title;
public LinearLayout placeholder;
public ViewHolder(View view) {
super(view);
view.setOnCreateContextMenuListener(this);
title = (TextView) view.findViewById(R.id.title);
placeholder = (LinearLayout) view.findViewById(R.id.placeholder);
}
}
#Override
public RecyclerAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_layout, parent, false);
ViewHolder vh = new ViewHolder((LinearLayout) view);
return vh;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Message item = mDataset.get(position);
holder.title.setText(item.getTitle());
int numImages = item.getImages().size();
if (numImages > 0) {
View test = LayoutInflater.from(holder.placeholder.getContext()).inflate(R.layout.images, holder.placeholder, false);
ImageView image = (ImageView) test.findViewById(R.id.image);
Glide.with(context)
.load("http://www.website.com/test.png")
.fitCenter()
.into(image);
holder.placeholder.addView(test);
}
}
#Override
public int getItemCount() {
return mDataset.size();
}
}
However, some of the items in the RecyclerView are showing images when they shouldn't be. How can I stop this from happening?
I do the check if (numImages > 0) { in onBindViewHolder(), but that's still not stopping it from showing images for items that shouldn't have images.
You should set imageView.setImageDrawable (null)
In onBindViewHolder() before setting the image using glide.
Setting image drawable to null fix the issue.
Hope it helps!
The problem is in onBindViewHolder, here:
if (numImages > 0) {
View test = LayoutInflater.from(holder.placeholder.getContext()).inflate(R.layout.images, holder.placeholder, false);
ImageView image = (ImageView) test.findViewById(R.id.image);
Glide.with(context)
.load("http://www.website.com/test.png")
.fitCenter()
.into(image);
holder.placeholder.addView(test);
}
If numImages is equal to 0, you're simply allowing the previously started load into the view you're reusing to continue. When it finishes, it will still load the old image into your view. To prevent this, tell Glide to cancel the previous load by calling clear:
if (numImages > 0) {
View test = LayoutInflater.from(holder.placeholder.getContext()).inflate(R.layout.images, holder.placeholder, false);
ImageView image = (ImageView) test.findViewById(R.id.image);
Glide.with(context)
.load("http://www.website.com/test.png")
.fitCenter()
.into(image);
holder.placeholder.addView(test);
} else {
Glide.clear(image);
}
When you call into(), Glide handles canceling the old load for you. If you're not going to call into(), you must call clear() yourself.
Every call to onBindViewHolder must include either a load() call or a clear() call.
I also had issues with RecyclerView showing wrong images. This happens because RecyclerView is not inflating view for every new list item: instead list items are being recycled.
By recycling views we can ruffly understand cloning views. A cloned view might have an image set from the previous interaction.
This is especially fair if your are using Picasso, Glide, or some other lib for async loading. These libs hold reference to an ImageView, and set an image on that refference when image is loaded.
By the time the image gets loaded, the item view might have gotten cloned, and the image is going to be set to the wrong clone.
To make a long story short, I solved this problem by restricting RecyclerView from cloning my item views:
setIsRecyclable(false)in ViewHolder constructor.
Now RecyclerView is working a bit slower, but at least the images are set right.
Or else cansel loading image in onViewRecycled(ViewHolder holde)
The issue here is that, as you are working with views that are going to be recycled, you'll need to handle all the possible scenarios at the time your binding your view.
For example, if you're adding the ImageView to the LinearLayout on position 0 of the data source, then, if position 4 doesn't met the condition, its view will most likely have the ImageView added when binding position 0.
You can add the content of R.layout.images content inside your
R.layout.message_layout layout's R.id.placeholder and showing/hiding the placeholder depending on the case.
So, your onBindViewHolder method would be something like:
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Message item = mDataset.get(position);
holder.title.setText(item.getTitle());
int numImages = item.getImages().size();
if (numImages > 0) {
holder.placeholder.setVisivility(View.VISIBLE);
ImageView image = (ImageView)holder.placeholder.findViewById(R.id.image);
Glide.with(context)
.load("http://www.website.com/test.png")
.fitCenter()
.into(image);
}else{
holder.placeholder.setVisibility(View.INVISIBLE);
}
}
Sometimes when using RecyclerView, a View may be re-used and retain the size from a previous position that will be changed for the current position. To handle those cases, you can create a new [ViewTarget and pass in true for waitForLayout]:
#Override
public void onBindViewHolder(VH holder, int position) {
Glide.with(fragment)
.load(urls.get(position))
.into(new DrawableImageViewTarget(holder.imageView,/*waitForLayout=*/ true));
https://bumptech.github.io/glide/doc/targets.html
I also had the same problem and ended with below solution and it working fine for me..
Have your hands on this solution might be work for you too (Put below code in your adapter class)-
If you are using Kotlin -
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
If you are using JAVA -
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
This works for me in onBindViewHolder!
if(!m.getPicture().isEmpty())
{
holder.setIsRecyclable(false);
Picasso.with(holder.profile_pic.getContext()).load(m.getPicture()).placeholder(R.mipmap.ic_launcher_round).into(holder.profile_pic);
Animation fadeOut = new AlphaAnimation(0, 1);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(1000);
holder.profile_pic.startAnimation(fadeOut);
}
else
{
holder.setIsRecyclable(true);
}
I was having same issue I solved by writing holder.setIsRecyclable(false).Worked for me.
#Override
public void onBindViewHolder(#NonNull RecylerViewHolder holder, int position) {
NewsFeed currentFeed = newsFeeds.get(position);
holder.textView.setText(currentFeed.getNewsTitle());
holder.sectionView.setText(currentFeed.getNewsSection());
if(currentFeed.getImageId() == "NOIMG") {
holder.setIsRecyclable(false);
Log.v("ImageLoad","Image not loaded");
} else {
Picasso.get().load(currentFeed.getImageId()).into(holder.imageView);
Log.v("ImageLoad","Image id "+ currentFeed.getImageId());
}
holder.dateView.setText(getModifiedDate(currentFeed.getDate()));
}
override fun getItemId(position: Int): Long {
return position.toLong()
}
override fun getItemViewType(position: Int): Int {
return position
}
This Works for Me
I Had the same issue and i fixed it like this:
GOAL : onViewAttachedToWindow
#Override
public void onViewAttachedToWindow(Holder holder) {
super.onViewAttachedToWindow(holder);
StructAllItems sfi = mArrayList.get(position);
if (!sfi.getPicHayatParking().isEmpty()) {
holder.viewFliperMelk.addSlider(new TextSliderView(mContext.getApplicationContext()).image(T.GET_MELK_IMAGE + '/' + sfi.getPicHayatParking() + ".jpg").setScaleType(BaseSliderView.ScaleType.CenterCrop));
}
if (!sfi.getPicSleepRoom().isEmpty()) {
holder.viewFliperMelk.addSlider(new TextSliderView(mContext.getApplicationContext()).image(T.GET_MELK_IMAGE + '/' + sfi.getPicSleepRoom() + ".jpg").setScaleType(BaseSliderView.ScaleType.CenterCrop));
}
if (!sfi.getPicSalonPazirayi().isEmpty()) {
holder.viewFliperMelk.addSlider(new TextSliderView(mContext.getApplicationContext()).image(T.GET_MELK_IMAGE + '/' + sfi.getPicSalonPazirayi() + ".jpg").setScaleType(BaseSliderView.ScaleType.CenterCrop));
}
if (!sfi.getPicNamayeStruct().isEmpty()) {
holder.viewFliperMelk.addSlider(new TextSliderView(mContext.getApplicationContext()).image(T.GET_MELK_IMAGE + '/' + sfi.getPicNamayeStruct() + ".jpg").setScaleType(BaseSliderView.ScaleType.CenterCrop));
}
}
I had a similar issue when getting pictures from the photo gallery and putting them in a recyclerview with GridLayoutManager(never had the issue with Glide). So in the adapter onBindViewHolder use a HashMap or SparseIntArray to put the current hashcode(this is the common thing that the recycled views have in common) and adapter position inside it. Then call your background task and then once it's done and before you set the image, check to see if the hashcode key - which will always have the current adapter position as the value - still has the same value (adapter position) as when you first called the background task.
(Global variable)
private SparseIntArray hashMap = new SparseIntArray();
onBindViewHolder(ViewHolder holder, int position){
holder.imageView.setImageResource(R.drawable.grey_square);
hashMap.put(holder.hashCode(), position);
yourBackgroundTask(ViewHolder holder, int position);
}
yourBackGroundTask(ViewHolder holder, int holderPosition){
do some stuff in the background.....
*if you want to stop to image from downloading / or in my case
fetching the image from MediaStore then do -
if(hashMap.get(holder.hashCode())!=(holderPos)){
return null;
}
- in the background task, before the call to get the
image
onPostExecute{
if(hashMap.get(holder.hashCode())==(holderPosition)){
holder.imageView.setImageBitmap(result);
}
}
}
So i am just providing an extension to this answer since there is not much space to leave it as comment.
After trying out like mentioned in one of above solutions i found out that, the real issue can still be addressed even if you are using a static resource(is not being downloaded and is available locally)
So basically on onBindViewHolder event i just converted the resource to drawable and added it like below :
imageView.setImageDrawable(ContextCompat.getDrawable(context,R.drawable.album_art_unknown));
this way you wont have an empty space on the view while glide/async downloader is loading the actual image from network.
plus looking at that being reloaded every time i also added below code while calling the recycler adapter class;
recyclerView.setItemViewCacheSize(10);
recyclerView.setDrawingCacheEnabled(true);
so by using above way you wont need to set setIsRecyclable(false) which is degrading if you have larger datasets.
By doing this i you will have a flicker free loading of recyclerview of course except for the initial loads.
I would like to say that if you send the ImageView and any load-async command (for instance loading from S3), the recycler view does get confused.
I did set the bitmap null in the onViewRecycled and tested with attach and detach views etc. the issue never went away.
The issue is that if a holderView gets used for image-1, image-10 and stops at the scroll with image-19, what the user sees is image-1, then image-10 and then image-19.
One method that worked for me is to keep a hash_map that helps know what is the latest image that needs to be displayed on that ImageView.
Remember, the holder is recycled, so the hash for that view is persistent.
1- Create this map for storing what image should be displayed,
public static HashMap<Integer, String> VIEW_SYNCHER = new HashMap<Integer, String>();
2- In your Adapter, onBindViewHolder,
String thumbnailCacheKey = "img-url";
GLOBALS.VIEW_SYNCHER.put(holder.thumbnailImage.hashCode(), thumbnailCacheKey);
3- Then you have some async call to make the network call and load the image in the view right ?
In that code after loading the image from S3, you test to make sure what goes into the View,
// The ImageView in the network data loader, get its hash.
int viewCode = iim.imView[0].hashCode();
if (GLOBALS.VIEW_SYNCHER.containsKey(viewCode))
if (GLOBALS.VIEW_SYNCHER.get(viewCode).equals(bitmapKey))
iim.imView[0].setImageBitmap(GLOBALS.BITMAP_CACHE.get(bitmapKey).bitmapData);
So essentially, you make sure what is the last image key that should go into a view, then when you download the image you check to make sure that's the last image URL that goes in that view.
This solution worked for me.

Best way to implement header over a RecyclerView using a grid layout?

I'm trying to implement the below image. My first thought was to have everything above the grid layout be the first row of the grid and use SpanSizeLookup to set the span size to the number of columns in the RecyclerView, but this feels like something that will give me a lot of problems.
I've been reading about putting a RecyclerView inside a NestedScrollView, people say it works, but I can't seem to get it to work properly. The scrolling doesn't seem to work right, I can't get the grid to even show up without setting a minHeight, but then it just looks bad.
Is there another option I'm not considering or is one of these the direction I should be going?
What kind of problems are you anticipating from SpanSizeLookup? You can implement it with a few lines as follows (I'd recommend using values from integers.xml for flexibility).
GridLayoutManager glm = new GridLayoutManager(getContext(), 3);
glm.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override public int getSpanSize(int position) {
return (position == 0) ? 3 : 1;
}
});
If your header layout needs views and fields that your regular layout doesn't have, you'll want to create separate views and tell your adapter about them. Something like this.
#Override
public int getItemViewType(int position) {
if (position == 0)
return TYPE_HEADER;
else
return TYPE_REGULAR;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
MyHeaderView view = (MyHeaderView) LayoutInflater
.from(parent.getContext())
.inflate(R.layout.my_header_view, parent, false);
return new MyHeaderViewHolder(view);
} else {
MyRegularView view = (MyRegularView) LayoutInflater
.from(parent.getContext())
.inflate(R.layout.my_regular_view, parent, false);
return new MyRegularViewHolder(view);
}
}
An example header view could be like this (you'd call bindTo() from MyHeaderViewHolder).
public final class MyHeaderView extends LinearLayout {
#Bind(R.id.image) ImageView imageView;
#Bind(R.id.title) TextView titleView;
public MyHeaderView(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.bind(this);
}
public void bindTo(String imageUrl, String title) {
Glide.with(getContext())
.load(imageUrl).into(imageView);
titleView.setText(title);
}
}
use StaggeredGridLayoutManager
use a different layout for your first item (the whole complex view)
get its layout params and setFullSpan
This makes the item as wide as the RecyclerView itself (similar to match_parent).
Set various click listeners in the specific ViewHolder that's responsible for this item. Using this approach would set the whole complex view to behave (scroll) as part of the RecyclerView while still making it available for (input) events.
You could have a look at Bookends
Or another(the way i usually do it) way would be to use a GridLayouManager with a SpanSizeLookUp. And use multiple ViewTypes i.e. one for Header,Footer and Items.
Go for 1 if you have only a single header and what a ListView-ish interface in your code.
Go for 2 if you are not sure about how many Custom ViewTypes you would be adding.It assures you have maximum scalability in the future.
If you are considering massive scalability,I suggest you read this article by Hannes Dorfman .

Android - Custom dynamically-generated compound View recreated in RecyclerView, causing poor performance

I'm using a custom CompoundView which extends LinearLayout to display items of a RecyclerView. Each item displays an article which contains multiple paragraphs and images. The CompoundView adds TextView or ImageView dynamically based on the data attached by CompoundView.setData(List<DataPiece> pieces), the number of which is unknown before data is attached. Each DataPiece object tells CompoundView whether it's a piece of text or an image. And here is the code for CompoundView.setData(List<DataPiece> pieces):
public void setData(List<DataPiece> pieces) {
removeAllViews();
for (DataPiece dataPiece : pieces) {
switch (dataPiece.getType()) {
case IMAGE:
ImageView imageView = new ImageView(getContext());
...
addView(imageView);
break;
case TEXT:
TextView textView = new TextView(getContext());
...
addView(textView);
break;
}
}
}
In the RecyclerView.Adapter.onBindViewHolder(), the data is attached to CompoundView by calling MyViewHolder.compoundView.setData(...). And it works fine when the RecyclerView is created.
However, for a CompoundView item with multiple ImageViews and TextViews, when I scroll away from it and then scroll back, the scroll becomes heavily unsmooth.
I guess it's because removeAllViews() in setData() is called, and the CompoundView creation for-loop is executed again by the recycler. But I don't know how to avoid this.
And I also wonder why the scroll is always smooth when using TextView(with Images) in a RecyclerView even it's recycled too.
Thanks in advance!
There are multiple considerations that could go into deciding what the best approach might be.
First, do you have an idea about the maximum number of items in the recycler's list? If it is just a handful, maybe you could ditch the RecyclerView approach and just add your CompoundView into a container hosted by a ScrollView.
Secondly - is the layout of each item fairly complicated (a.k.a. are there many TextViews, ImageViews etc. in it)? If yes, maybe you could take an approach that would resemble an ExpandableListView - show a summary as each list item and expand to the full layout of the item on click.
Thirdly - if none of the above is acceptable and you still want to go the current approach - don't construct/add your view in the binding method. Do it in the onCreateViewHolder, when the system expects you to construct your view (I don't know for sure but by the time you're called on onBindViewHolder your view might have been already added to the hierarchy and any hierarchical change to it has a ripple effect on its containers - but don't take my word for it, I don't actually know the view is already added, it is just an assumption). You will have to assign each item a different type, so that in onCreateViewHolder you could match the view type with the supporting data (for the addition of the corresponding number of child views); create the view from scratch each time - this way you don't need to call on removeAllViews. Something like(I left out parts of the adapter that are not relevant to the case):
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
ArrayList<DataPiecesList> mItems;
public RecyclerViewAdapter(ArrayList<DataPiecesList> items) {
mItems = items;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
CompoundView compoundView = new CompoundView();
List<DataPiece> dataPieces = mItems.get(viewType);
for (int i = 0; i < dataPieces.size(); i++)
{
// construct TextView or ImageView or whatever
compoundView.add(child);
}
MyViewHolder view = new MyViewHolder(compoundView);
return view;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) {
CompoundView compoundView = viewHolder.itemView;
DataPiece dataPiece = mItems.get(i);
for (int j = 0; j < compoundView.getChildCount(); j++)
{
compoundView.getChildAt(j) <- dataPiece.get(j);
}
}
#Override
public int getItemViewType(int position) {
return position;
}
#Override
public int getItemCount() {
return mItems.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder {
...
public MyViewHolder(View itemView) {
super(itemView);
}
}
}
RecyclerView is supposed to reuse the views. It will be slow if you throw away the already created TextView / ImageView objects and create new ones every time.
It sounds like you need a RecyclerView with multiple view types. The idea is to create multiple view holders - some of them with ImageView, the others with TextView. You'll have to override the getItemViewType(int position) method of your adapter - it should return different values for the IMAGE items and the TEXT items. The onCreateViewHolder(ViewGroup parent, int viewType) receives a viewType parameter so you know which type of ViewHolder to create there. In the onBindViewHolder(VH holder, int position) you could assume that the holder passed to you is the correct type (i.e. the type with TextView for TEXT items and the type with ImageView for IMAGE items), so there is no need to remove its child views and create them again.
There is nice article about RecyclerView's Adapters with multiple view types here.

Android - How to stop images reloading in Recyclerview when scrolling

I was creating an app for loading images from internet in cards. I am using Recyclerview for listing purpose. For downloading images, I have tried Picasso, universal-image-downloader and other custom methods but everywhere I have this problem:
Whenever I scroll down and up the images in imageview are getting reloaded. I have even kept a condition to check if the imageview contains an image but that also doesn't seem to work or my code may be faulty. But I am not able to find a solution to this problem. Below I have attached the 'onBindViewHolder' function of my code which I think is the problematic part. Here images is a string arraylist containing image urls.
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.default_card, parent, false);
ViewHolder viewHolder;
viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String url = images.get(position);
if (holder.imgView.getDrawable() != null) {
//Do Nothing
} else {
ImageLoader.getInstance().displayImage(url, holder.imgView);
}
}
You can use .fit() with Picasso. Hope that solves your problem
Picasso.with(persons.get(i).context)
.load(persons.get(i).photoId)
.placeholder(R.drawable.placeholder)
.fit()
.into(personPhoto);
At first, there is a bug in your code - you have to remove the condition from onBindViewHolder. All ImageView instances has to be updated each time they are about to display.
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String url = images.get(position);
ImageLoader.getInstance().displayImage(url, holder.imgView);
}
The reloading part:
The problem is, that you are probably not using any cache (in-memory, or disk based). I would not elabarote on this a lot, caching of images in collection views is a really common problem and I'm sure a quick StackOverflow search will give you a lot of results.
Just in particular - Universal image does not have caching enabled by default You can turn it on this way.
DisplayImageOptions options = new DisplayImageOptions.Builder()
...
.cacheInMemory(true)
.cacheOnDisk(true)
...
.build();
ImageLoader.getInstance().displayImage(imageUrl, imageView, options); // Incoming options will be used
Or you can set it globally for your application, just check the docs.
You can use this to solve your issue.
mRecyclerView.getRecycledViewPool().setMaxRecycledViews(0, 0);
If you don't want to use Cache, you can use setItemViewCacheSize(size) and mention the size where you don't want to reload. That's it. Recyclerview version 24.0.0

Categories

Resources