How to avoid refreshing of cells on when calling notifyDataSetChanged() for PinterestLikeAdapterView?
Asked Answered
O

4

7

Background

I'm using the PinterestLikeAdapterView library to show some images from the internet, which is like a gridView but with different height for each cell.

The problem

Since I use this library to show images from the internet, it's crucial that when calling notifyDatasetChanged won't cause a mess on the views.

For some reason, calling this function would call the getView() method with different positions for the views. for example, even though i didn't scroll at all, and call notifyDatasetChanged (or addAll in case it's an ArrayAdapter), for position 0 it will take what was the view of position 8, for position 1 it will take the view of position 7 , and so on...

This makes the whole grid to refresh its images, and so it ruins the UX.

Usually, in both gridView and listView, the way to overcome refreshing is to put the position that was used for the view inside the viewHolder, and if they are equal, it means that they still match.

for example:

... getView(...)
  {
  //<=inflate a new view if needed 
  //avoid refreshing view in case it's still the same position:
  if(position==holder.position)
    return rootView;
  holder.position=position;
  //<=update the view according to its data
  ...
  }

However, here they re-use other views in a different order so this trick won't work here.

Because of this issue, not only i get refreshes of almost all of the visible views, but since i use DiskCacheLru library, it crashes since it tries to put 2 identical inputSteam data into the same key using 2 threads.

The question

What can I do? Is this a known bug in the library?

Maybe I'm using a bad way to overcome refreshes?

for now, i use memory cache to at least get items that were cached before, but that's more like a "cure" than a "vaccine"...

Otranto answered 8/10, 2013 at 9:17 Comment(6)
You mean that order of views is in a mess?Tyre
yes and no. the grid somehow shows them well. it's just that it calls the adapter in a weird order. maybe i didn't explain the example well: even though i didn't scroll at all, and i call notifyDataSetChanged() , the getView method will call on position 0 and give me the view of position 8 instead of the real view that was used for position 0. the problem is that because of this, i can't overcome the refreshing of the views , and so it looks like it reloads the images. i think memory cache could help, but it's not really a fix for this as there might be other cases i didn't think about.Otranto
I always thought that after notifyDataSetChanged, you do not get old convertViews in getView.Inaudible
@SherifelKhatib it will get it too here, but in a weird way - for each position you get the wrong view, so you can't use the older state of it when it already matches the position. this means that updating of the cell is unavoidable in such a case.Otranto
It is perfectly normal to get a different view but I thought they should be null. Try this hack: 1- override getViewTypeCount() and return getCount(); inside it. 2- override getItemViewType(int position) and return position; inside it.Inaudible
@SherifelKhatib i don't understand how could making new types help, as it will force creating more new views to be cached. what i need is to avoid updating views when not needed. also, i already use getViewTypeCount() and getItemViewType() because i need it for different types of information, but even when it's a single type i get the same problem.Otranto
K
2

Short answer:

Use an image loading library like Picasso that caches most recently used images in memory, so they don't need to be reloaded from the network.

Long answer:

AdapterView does something called View recycling, where Views which are no longer needed to display a position are re-used to display another. (For example, as you scroll down, Views that disappear off the top of the screen are reused for new positions at the bottom of the screen.) Because of this, it's normal for getView() to be passed the same View for more than one position.

This is done for performance reasons: Inflating new Views is hard and takes time, so AdapterView tries to do it as infrequently as possible.

When using a holder, you store references to ImageView and TextView children inside the item's View, so you don't have to look them up with findViewById() each time - you don't usually store anything specific to a particular position, because the View and its holder will often be used for different positions.

Now, when you call notifyDataSetChanged(), AdapterView assumes that the data set has completely changed. The image that was associated with position 8 may no longer be present, or it may be associated with position 12 now. Consequently, all the existing Views are scrapped - but because AdapterView would still like to avoid inflating new Views, they're re-used to display the new data, with no regard for what position they were displaying previously.

This explains why getView() is being passed the same View for different positions, and why visible positions are being refreshed when you call notifyDataSetChanged(). But how to avoid having your images refresh, ruining the user experience?

Use an image loading library like Picasso that caches most recently used images in memory, so they don't need to be reloaded from the network. The refresh will still happen, but it'll be instantaneous.

Keelin answered 19/10, 2013 at 18:19 Comment(2)
i know the way the adapterView works, and i used the position in the viewholder to avoid refreshing of views that are already in sync with the data. the problem is that this trick doesn't work on this library as it messes with the order of views even though no scrolling was performed. about the library you've suggested, it seems nice, but i'm not sure if it could help since i need to have the file in both small size and large size (fit to screen) so i download the file and then do the downsampling accordingly. when pressing an image, it will get to show the full version of it.Otranto
Thanks a lot.. !! its working fine when i used picasso instead of glide and universal image loader.Subcontract
T
1

View getView(int position, View view, ViewGroup parent) will be always called ascendingly, after notifyDataSetChanged().
I guess that, the order of finishing download task will cause this problem.
As you mentioned in your question, keeping the position is a good way to avoid this problem.

Here is another way to solve it, also re-use the imageviews.

Keep a weak reference of each ImageView in download task.
Then wrap the download task in a dummy ColorDrawable.
When getView is called, set the dummy ColorDrawable to ImageView, and start the download. When download is complete, set the downloaded image back to the referenced ImageView in OnPostExecute().

Explanation
http://android-developers.blogspot.jp/2010/07/multithreading-for-performance.html
Source code
https://code.google.com/p/android-imagedownloader/source/checkout

Tyre answered 9/10, 2013 at 3:22 Comment(1)
no the tasks have nothing to do with that. the tasks will actually get cancelled since the position of the getView doesn't match the one of the viewHolder. what is going on here is something like scrolling, even though no scrolling was performed. as a side note, i would like to know a bit more about the idea you've presented, but it can't solve the problem i've shown.Otranto
D
0

There is a very good example on PinterestLikeListView in GitHub

Here is the library StaggeredGridView

A modified version of Android's experimental StaggeredGridView. Includes own OnItemClickListener and OnItemLongClickListener, selector, and fixed position restore.

You can get library project here library

and you can get Demo project Here

This is very good open source project, so you can use instead of PinterestLikeAdapterView

enter image description here

Hope this library is going to help you out.

Drought answered 20/10, 2013 at 15:38 Comment(1)
sadly this library has even more problems, such as showing empty cells and NPEs . i've already written a post about searching for the best library that shows such a grid, here: #18558220 . none of the libraries i've found is bug-free. each has its own weird issues.Otranto
O
0

seems that the authors of this library have fixed it, after some time i've reported about it:

https://github.com/huewu/PinterestLikeAdapterView/issues/8

Otranto answered 11/12, 2013 at 7:36 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.