I'm writing an app that shows images in a listview after downloading them from the internet. If an image has not downloaded when getView() is called, I have a placeholder image. The problem with this is if getView() is called before the image is downloaded, and the user does not scroll anymore, the placeholder image will stay there until the user scrolls away and comes back to call getView() on that position again.
I'm looking for a way to listen for the file being created and when it is, update that row. Is this possible? I've googled around, but cannot seem to find anything. I would imagine I would have to call an adapter.notifyDataSetChanged(), but I don't know how to listen for a file being created.
Related
I have a list which I use recyclerView to display. Each row in the list displays an image that is downloaded from a remote server. In order to speedup the display of the list I am downloading the images in the background. Each time the bind() wants to display the image I call a method that starts an asynchronous task that download the image and in the onPostExecute() method I call setImageBitmap() of the image to display the bit-mapped downloaded.
The problem is as follows
As I scroll down, the recyclerView loads more images that fits the screen and when the setImageBitmap() is called the list jumps up, so that the last 1 or 2 items in the list almost never displayed.
When I disable the call to setImageBitmap() there is no problem in displaying the last items (the list now is without images), and scrolling up and down the list.
The problem also shows up when I scroll down very slow. I limited the screen to show only 4 items, and as I scroll down to the 6th or 7th item suddenly the display jumps and displays again from item 2 to 5.
What is going on there and how can I make the display to scroll smoothly and without jumping all over?
There problem here comes to the fact your recycler view at the time it is inflated has size X, when you load your image the size changes but the LayoutManager is not aware of this change, hence it won't update your view.
You should post a snippet of your code, it will help to clarify.
TL,DR : How can I force the recycler view to call the onBindViewHolder method again, at least for the visible items?
Calling notifyDataSetChanged() will make the list lag for some milliseconds, is there a better way?
Thanks.
I have a layout with an ImageView.
When ever bind is called for the imageview, I send a request to a server in order to get an image.
When the image is loaded, I save the bitmap in the ViewHolder , T variable.
And in the bind method I check if variable.getBitmap() is null or not, and if it is I will set the imageview.
Now If I scroll my list the images are going to be loaded, but If not, the imageviews are still blank, because onBindViewHolder wasn't called again.
Thanks.
notifyDataSetChanged() is definitely the right way. Maybe notifyItemChanged() is even better because it only binds the selected item. If I get your question right, it seems that you do some things on the Main Thread which shouldn't be done there. Remember: Never do potential lengthy actions on the Main Thread, but always use something like AsyncTask.
I think it is a better approach to download the images asynchronously and then cache it, so you don't have to download it every time your Views are recycled. There are libraries for it.
While the images are loading you could show a ProgressBar or something else which signalizes the user, that the image is being loaded.
This is not a big issue, but it looks strange. In a list or gridview I load images with a delay (e.g. loaded from remote place). I'm recycling the items, using convertView. When I scroll down fast, I see the old images repeated, until the correct images are fetched and replace them.
Is there a way to change this? I tried, for example, at the very start of getView() (after initializing convertView, if necessary), to set the imageView visibility to GONE. And set to VISIBLE after the image has been fetched. But for some reason, this doesn't work (still looks the same).
Thanks!
Edit: I'm not even sure if this is normal behaviour when using recycled views, or if I'm doing something wrong. It doesn't look like a bug when the internet connection is fast, or when fetching from the file system. Then the new images are loaded very fast and the user doesn't see repeated items. But on a slow internet connection, it looks like a bug.
Before setting image in row just check if the image belong to the content of view.
I had the same problem sometime back and I had to change my image downloader so that it takes image url as well as meta data so that once download is complete, i can compare meta-data with view's data.
I have a Gallery, which has an adapter connects to it. In the getView method, i have a custom layout, so that I can i have image and caption displayed together
The image is downloaded from an URL, which is done asynchously, and working as expected.
Currently i make each item to fill the screen, so i only have one item display at a time, basically i want to make it like a slide show.
let me be clear, currently i have an activity, and it only has one View, which is a Gallery.
problem occurs when I am swiping, the image bounces and stays at the same image. i need to swipe many times, hard, and long swipe, then i can get to next image.
i put a debug message in my custom adapter in the getView(), it seems getView is getting called many times (4 times), and position being passed is either the current position or the previous one, which explains why i am stuck at the same screen.
if i remove the remote downloading image part, or just use a static image form the phone, i don't have any more issues, in fact, the getView only gets called once, with correct position.
i am very frustrated, not sure what the problem is, could it be because i am downloading image asynchously, which will cause the image to update which causes getView to get called again to redraw itself?
i am not sure..
please help
Unfortunately this is a bug with gallery. Listviews will scroll nicely regardless of data being updated asynchronously. However, the gallery is just not coded up to par with the listview
When the gallery tries to update a visible view (due to your image loading callback) this view will "snap" back to the focused position. If you are changing the view in any way when it is scrolling it will snap. This is likely why you have to scroll hard to get away from the current view. It is trying to perform a callback on your view and only scrolling fast will prevent the callback from occurring before you move away from that view.
I've reported this bug a while ago here:
Android Issues
There are a few workarounds posted in there you can try if you are set on using a Gallery.
Unfortunately it hasn't gained attention from the Android developers.
It seems the issue is caused with views being set to "wrap_content" and the gallery having to remeasure/redraw its views
I have since migrated away from using the gallery and instead use a ViewPager. It is much easier to manage and you don't have to worry about this problem. This has been a known problem with the Gallery since the gallery was first introduced. I have no idea if this was fixed in any of the newer Android versions (3.x/4.x). As of 2.3.7 it is not fixed.
At first you should note: Any AdapterView (Gallery, ListView etc) doesn't guarantee that each time getView() method is called it will pass the same View instance parameter. It only guarantees that each view will have the same type (see Adapter.getIntemViewType() method docs)
So, when you start image downloading you should only specify position of the element. Then after the image has been downloaded you should bind specified ImageView with downloaded image in Adapter.getView() method call.
Take a look to the ImageDownloader from the Android samples here
The other approach is to use WeakHashMap in order to contain map of adapter views to its positions. I can provide you with code samples if you need.
I have a ListView, which display pictures from Internet, in Adapter's getView method, I will start the Async download thread to download the picture,since I don't know the exact time of downloading finished. As you know, when user touch the screen, the ListView will refresh itself and call Adapter's getView method, then the ListView has been refreshed. But if the user does not touch the screen, even the picture already been downloaded, the ImageView still shows nothing.
So, my question is, what is the graceful way to refresh the ListView after downloading has finished?
You'll need to call notifyDataSetChanged to refresh the underlying views.
You need to load the image in the image view in a separate thread. Thus image will be loaded without touching it.