I'm building my first app based on material from http://javatechig.com/video/json-feed-reader-in-android.
Everything goes quite ok so far, but there is a bug in thumbnails. When I scroll list, random thumb images quick "refresh" with random different thumb and back to original one. Any suggestion why it behaves like that?
You should take a look at the following page:
http://developer.android.com/training/displaying-bitmaps/process-bitmap.html
The part Handle Concurrency explains everything:
Common view components such as ListView and GridView introduce another
issue when used in conjunction with the AsyncTask as demonstrated in
the previous section. In order to be efficient with memory, these
components recycle child views as the user scrolls. If each child view
triggers an AsyncTask, there is no guarantee that when it completes,
the associated view has not already been recycled for use in another
child view. Furthermore, there is no guarantee that the order in which
asynchronous tasks are started is the order that they complete.
So you are right. This is actually a bug of this sample which hasn't got a proper load/display logic.
Related
I have an android app that I'm developing using Xamarin. The app contacts the server and, via web service (SOAP), receives a list of objects. Currently, in my axml file I have just linear layout (ll) within scrollview tags and nothing else. In the code, I loop through the collection and new up the elements that I want and attach it to a layout. Once I'm done with each record, I attach (i.e.AddView) to the master layout (ll). Everything works.
I have a couple of concerns and I appreciate some feedback on it.
1) Each object in the list contains an URL to an image online. Currently, for each object, my process downloads the picture individually. Would ListView give me any advantage of reusing (caching , etc.) an already downloaded picture even though other attributes of the objects are different? Will there be any gain in terms of network utilization if I switch to ListView?
2) Is drawing elements by hand (like I'm doing) an acceptable best-practice?
Thanks all.
Definitely use a ListView. There is a great article here by Lucas Rocha that outlines exactly why ListViews are beneficial and how to make them perform even better. To give you a few examples, ListViews minimize the number of view inflations you do, and they only create the list items currently visible on the screen or about to become visible on the screen.
This is a huge improvement from your approach, since your current method would load every element in the list before presenting the activity to the user. Therefore, drawing elements by hand like you're currently doing is definitely not best-practice.
Also, for displaying images from URLs in your Xamarin app, I highly recommend that you use the Xamarin component UrlImageViewHelper. Despite being incredibly easy to implement in your app, it will improve performance drastically since it takes care of image caching and async image loading.
For the development of my app, I realized I needed a complicated view (let's call it foo), it contains three ImageButtons, a progress bar, and three TextViews, all of which are dynamically changed by interacting with the same view's elements. To make this work, I extended foo from RelativeLayout, dynamically created the sub-views then added them to foo (this.addView(...)).
What I planned to do next was add them dynamically to a ScrollView. I did this and put three foos for testing. The result was extreme lag. I'd press an ImageView (which should change its image on press), and it would take 2 seconds to do so.
My final aim would be to support 50 of these foos at a time and have them work smoothly, with the user having the option of loading more (without overwriting the previous ones) if he/she so chooses. All interactions will use the internet (I dunno if that's relevant), but the testing was done with all the network tasks commented out.
My questions are thus:
Is the strategy I was using (ScrollView & add foos to them) viable, and the lag is from some other issue (the specific code in question, in which case I'll provide some code)? Or is it really a bad idea to do that?
What would be the best way to reach my goal here (assuming 1 is bad)?
What I already know:
I've researched my problem a bit, and most online sources recommend using a ListView. I didn't read much into it but from what I got:
I'd have to redo the design using xml rather than dynamically
The different components and their values will be stored each on it's own array which is extremely unacceptable in my situation (changing the sub-view's values should be done very simply and should not appear in the main activity)
I can't (or it's difficult to) set OnClickListener's for the different sub-views (as only the main foo view will get one)
I also tried this method (ScrollView and add to Views) with another View and had 20 of them run at the same time seamlessly, but that one had been extended from View and only used canvas to draw text with no sub-views.
Thanks in advance.
This is not a code problem, I interpret the guidelines as that being OK.
I've been researching a way of building an infinitely scrolling calendar-like view in Android, but I've reached an impasse.
Right now my dilemma is that most of the similar views available have their children placed relative each other in a recurring style. With this I mean:
item 4 comes after item 3, which comes after item 2, and there is constant padding/margin between all items.
What I need is a way to produce an infinitely long scrollable view that may, or may not, contain items. The items should be placed at variable positions within the view. The best way I can describe a similar looking view is a one-day calendar-like view that is infinitely scrollable.
So far my best two bets are using the new RecyclerView with a custom LayoutManager (this seems very complex and still not perfectly documented by Google though). I like this approach because, among other things, it is optimized for displaying large sets in a limited view.
My other solution would be to build a completely custom View. However, with that solution I loose the adapter unless I build a container view (which is probably more complex than building a layout manager).
How would you go about solving such a problem? Tips are appreciated, I don't need code examples, just ideas which path is the best to solve this problem.
Thanks.
Apologies if I've misunderstood the guidelines
Edit: How I resolved this problem
My first solution to use RecyclerView with a special Decorator seemed promising, but it remained a "hack" so we decided not to go for that solution since we were afraid of the complications that it would create down the line.
To solve the problem I went with a SurfaceView instead of an Adapter, this means having to rewrite all the adapter-functionality for my SurfaceView but it seemed to be the best way of solving this issue of very custom drawing and layout managing for my use-case.
It still would be nice to build a custom Viewgroup that can handle this kind of layout problems.
ListView and ListAdapter are based on a fixed list, so the current infinite-scrollers just keep adding more and more data to the end of the list.
But what you want is scroller similar to Google's Calendar app which has a bi-directional infinite scroller. The problem with using ListView and ListAdapter in this case is that if you add data to the front of the list, the index of any one item changes so that the list jumps.
If you really start thinking about this from the MVC perspective, you realize that ListAdapter does not provide a model that fits this need.
Instead of having absolute indexing (i.e. 1, 2, 3, 4, etc), what you really want is relative indexing, so instead of saying "Give me the item at index 42" you want to say "here's an item, give me the five items before it". Or you have something like a calendar date which is absolute; yet — unlike your device's memory — it has effectively no beginning or end, so what you really want here is a "window" into a section of that data.
A better data model for this would be a kind of double-ended queue that is partly a LRU cache. You place a limit on the number of items in the structure. Then as prior items are loaded (user is scrolling up) the items at back end are pushed off, and when subsequent items are added (user is scrolling down), items at the front are pushed off.
Also, you would have a threshold where if you got within a few items of of one edge of the structure, a "loadNext" or "loadPrevious" event would fire and invoke a callback that you set up to push more data onto the edge of the structure.
So once you've figured out that your model is completely different, you realize that even RecyclerView isn't going to help you here because it's tied to the absolute indexing model. You need some sort of custom ViewGroup subclass that recycles item views like a ListView, but can adapt to the double-ended queue. And when you search code repos for something like this, there's nothing out there.
Sounds like fun. I'll post a link when I get a project started. (Sadly, it won't be done in any timely manner to help you right now, sorry.)
Something that might help you a little sooner: look at Google's Calendar implementation and see how they did it: Google Calendar Git repo
What you may be searching for is a FragmentStatePagerAdapter , where you can implement a swiped view, meaning when the user (for example)swipes to the right, a completely new view is displayed.
Using a FragmentStatePagerAdapter , you can handle a huge amount of views without overflowing the memory, because this specific PagerAdapter only keeps the views' states and is explicitly meant to handle large sets of views.
Keeping your example of a calendar, you can implement swiped navigation between for example weeks and generate the week views on demand while only keeping for example the year and the week's number as identifiers.
There are plenty of online tutorials for Android, maybe you have a look at this one
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"...
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.
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
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
Hope this library is going to help you out.
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
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.