I am trying to implement an infinite Carousel using the ViewPager component. I based on the one created by Antonyt but there is a problem using less than 4 views. As the view is already in place. Must be a way to trick the Viewpager to draw the same view/page in different places.
I ran into the same problem, couldn't find the solution myself. But i guess it is impossible to do by using only 1-4 views. What the problem is that all the views (for 1-4 images in repetition) will be instantiated at once(or atleast in a very short interval) this interferes with the image loading process because the prev load hasn't finished, so the prev one returns a damaged view. You may handle this something like this though.
1) right after instantiation, use handler with delay of 100-200ms to flip through views setCurrentItem() again and again(around 10 times) in either direction, this way the original 4 will be out of range(if you have off screen page limit, which i hope you have beacause of infinte nature of your code) and will be created one by one later on.
2) Use multiple buffer objects,
1 image-8 buffers
2 images-4 buffers for each
3 images-2 buffers for each... something like this.
neither is an ideal solution but both worked for me but. I would like to know what you tried too.
The solution is to use the same child view in the ViewPager. And to do that without having the same child already assign to a parent is to use a ProxyView with the real view inside. Extends that Fake View from ViewGroup and override the Draw method will do the trick!
You may need to do some works in OnDestoryItem method to re-attach item which have been detached.
I have already post my solution here,https://github.com/antonyt/InfiniteViewPager/issues/2
Try the following trick which I've used successfully to make a (faux)infinite ListView.
In your adapter's getCount() method, return Integer.MAX_VALUE.
Then in the adapter's instantiateItem() or destroyItem(), Use position % datasource.size()
This trick was taken from HERE.
Related
I'm trying to prevent my ViewPager's PagerAdapter from destroying Views it already loaded once.
If I understood it correctly, the method ViewPager.setOffscreenPageLimit(1) will create and cache a page on each side of the selected view.
This is okay for me, except for the part that if I for example jump to page 5, pages 4 and 5 will be created and cached, but pages 0 and 1 will be destroyed.
I've tried commenting out everything in my pager's destroyItem() method, and caching in a HashMap the views created in instantiateItem(), and returning them as-is if they are found on the HashMap.
This seems to be working OK, but I'm not sure if there are any downsides to doing that this way, apart from the higher memory usage, which is hopefully not a problem here.
My app is actually an application framework and I'm going to leave that decision to the final developer, leaving ViewPager's default behavior in case he/she doesn't want to apply this caching behaviour. Some clients are pretty unreasonable and they have a huge number of views in everypage. I've tried to convince them to work their way around in different pages since Android's View creation is costly, but as I said, they're unreasonable.
Do you guys know of any good way to do this?
For some reason (that I can develop if you want/need) I have to redraw all the chart periodically. So, I use removeAllSeries then addSeries, plus removeAllViews then addView. It works but the problem is that addView adds the view not by simply refreshing all pixels of the tablet but with a sort of "animation" that puts firstly the View a little bit (2 or 3 pixels) shifted to the right and then it takes the right place. The consequence is that, everytime I redraw my graph, it looks as if there is a "vibration" (it's not fluid).
Do anyone have some issue? Could this undesired "animation" be related to how the addView method is done?
there are three ways:
redrawAll() method. Maybe it is protected, but you can overwrite and
make it public
change the data in series (appendData or resetData). The Graph will
automatically rerender.
removeAllSeries + add new series. No need to call
removeAllViews/addView. Take a look at the GraphViews-Demos project,
there is an example about that.
Cheers
Thank you for your answer ! I actually just simply overrided the ValueDependentColor() method directly in my main activity. The color depends on a timer. So, when I reset data after x seconds the graph rerenders as you said in your "2."
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 am building an application where I need to display signal traces in real time (think the kind you see on cardiac monitors in hospitals). For my line animation to appear smoothly I'd like the frame rate to be around 15 frames per second (I did some experimenting to come to the conclusion this was the lowest acceptable frame rate). It probably does not matter in the context of this post but I potentially have numerous such view in a ListView (~20 is common with about 5 being displayed each time).
Until now I've been doing this using a custom view, extending the View class. I have a thread in the containing fragment that calls invalidate() on the view every ~70ms that redraws the view. This is not causing problems per se as I've optimized my onDraw() function to run in under 2ms most of the time.
Now I have added a Spinner to the fragment and while debugging it I noticed that once I opened the Spinner the adapter was constantly hitting getView() calls, even though I was not touching the Spinner (i.e. open but not scrolling) and also lagging a bit. This led me to realize that my whole fragment was being redrawn every ~70ms which to me sounds bad. Now the questions:
Is there a way to trigger onDraw() on a child view without it causing a redraw of the complete hierachy?
Should I just resort to a SurfaceView? (that would not cause a complete view hierarchy redraw, right?)
I've noticed that the SurfaceView is not HW accelerated. Does that matter if I'm only using basic draw functions (drawLine() and drawText())?
Would GLSurfaceView be any better in my case?
I'll try to answer my question so that it might be useful to other people who might encounter the same.
Containing a bunch of SurfaceViews inside a ListView is a fail. It seems to me since the SurfaceView drawing is not synced with the rest of the UI you will get black lines flickering between views when you scroll (which is probably since the SurfaceView is reassigned to new data source via getView() and displayed before it gets a chance to redraw itself). Triggering a redraw inside getView() was not enough, apparently.
Delyan's first comment to my question was valid even though it might not always apply. If I go through each view in the ListView by hand and call invalidate() on it the redraw will not bubble up through the hierarchy (I did not need the invalidate(l,t,r,b) signature, but it's probably a smart idea to try it out if you're having problems with excessive redraws). So, even though Romain Guy mentions in his talk that invalidate() will bubble up to the root it's apparently not always the case. Delyan's comment about different implementation of HW/SW might be the reason - I'm rendering my views in HW.
Anyhow, to work around the issue I created this method inside my adapter:
public void redrawTraces(ListView lv) {
final int viewCount = lv.getChildCount();
for(int i=0; i < viewCount; i++) {
final View stv = lv.getChildAt(i);
if(stv != null) {
stv.invalidate();
}
}
}
Where the views were not SurfaceViews. Finally, to make it clear what I was doing wrong, I was doing this:
adapter.notifyDataSetChanged();
This call did bubble up and caused a redraw through the entire hierarchy. My assumption was that this was roughly the equivalent of calling listView.invalidate() in terms of updating the whole hierarchy; perhaps that's correct I didn't look into it.
I have a ListView with custom Adapter. To be honest, I have many of them at the same time on screen, and my Tegra 3 device started to lag, what made me really confused... I found than in each ListView's Adapter the getView() method is called for all visible rows every time any animations runs on screen. That gives me like few hundreds of calls per second! Digging more, most of these calls are due to measure() and onMeasure() calls of ListViews' parents, and - this is tke key - they are useless, because all the layouts of my ListViews
have const size.
So my question is: how to eliminate these calls? Of course I want to leave proper calls alone (caused by adding items to Adapter and notifyDataSetChanged() ).
I've tried almost anything, but either the whole list doesn't draw itself (when I overriden it's onMeasure() and forced to returned const size without calling super.onMeasure()) or stops updating at some time.
How you implemented the getView() method? If you implement it in the correct way there should be nearly no lagging.
Check out this really really good video:
http://www.youtube.com/watch?v=wDBM6wVEO70
Slides: http://dl.google.com/googleio/2010/android-world-of-listview-android.pdf
As Romain said, work with it not against it. Best is to leave measure() alone and focus on your adapter.
Thats how ListView is implemented.. I don't think that will cause a performance Overhead.. Provided you do things properly there..
For example..
Don't instanciate LayoutInflator inside GetView Method, Do it at class level..
And Inflate View Only if the convertView==null or else just return convertView.. Inflating view is a costly process....
Well like you said these calls are due to measure() and onMeasure() calls of ListViews parents and I'm sure you are using height=wrap_content also with wrap_content on height your ListView will check without stop if your height has changed.
So the solution is to put the height=fill_parent.
I hope this helped you.
The underlying reason for this is that ListView.onMeasure() calls AbsListView.obtainView(), which will request a view from your list adapter. So if your view is being remeasured through animations, your performance will be very poor.