I have implemented a ViewPager in my app, and aside from the swipe paging, I have buttons for "Next" and "Back", with a simple onClick method with the only line being a setCurrentItem for the ViewPager.
While the paging animation is completely smooth during swiping, it is more or less instantaneous (just flips instead of scrolling) when I click Next or Back. There is no visible "scrolling" motion, or sometimes barely visible, however not even close to smooth. It does not hurt the usability of the app in any way, since the transition still happens immediately, but visually it looks a lot less appealing, and the user experience suffers.
Now, I suspect it has something to do with the app drawing the next View and therefore "skipping" the animation. My instantiateItem uses LayoutInflater and a simple switch statement to inflate each view (4 pages at most), and I am loading custom ListViews inside each case in the switch statement. The custom ListViews are at most 3 items long, with a TextView and an ImageView in each row (ImageView resource is 5kb in size). To me it seems this amount of objects should not be reason enough to slow down the paging animation.
Aside from that, I also have a custom background assigned to my app in styles.xml, and this is the background used through every activity (160kb in size).
Those are the only things that I think could be slowing down the app, since the onClick method contains only one line (setCurrentItem), so nothing aside from going to the next view is happening in the app, and the instantiateItem is the simplest implementation of the method possible that I know of.
Things I've tried:
Setting the ViewPager's setOffScreenPageLimit to 4, which I thought would pre-load the views and eliminate any loading in between paging, but in the end setting it to 0 seemed to make the thing slightly more likely to show at least a frame of animation
Optimizing the custom ListViews by using convertView to reuse old views, while it did result in more of the animation being shown, it still wasn't completely smooth, varying from "almost perfect animation" to "instant flip"
Removing the custom background, which together with the above two fixes lead to an almost good enough solution, but still resulted in flips every few steps.
The tinkering described above leads me to believe it has something to do with the views being loaded, but I've not found a good solution for pre-loading these to the desirable effect.
Knowing my newbie programming skills, I know there is a sure fire way to solve this without resorting to gimping the design of the app itself, but reading through the documentation, Stack Overflow questions, Googling for advice, I've just ran out of places to look to.
To sum up this probably too verbally descriptive post my main questions are:
Is there a way to ensure the animation of the ViewPager is executed smoothly? A separate thread perhaps? As I understand though, UI stuff should only be on the main thread, where it already is, and nothing else aside from the view loading is executed anywhere in the code when paging.
Should I be using a different way to load the pages? Would Fragments help?
Thank you for any advice.
ps. The lack of code is due to the methods in question being the most basic implementations possible, but I will provide the code if necessary.
Related
I am attempting to create a MultiDirectional Paging application that is based off an Activity that contains a Support ViewPager and within that are Fragments that contain VerticalViewPagers. Both ViewPagers are using a FragmentStatePagerAdapter and the data is coming from a ContentProvider that is loaded via CursorLoaders. The vertical fragments contain just a simple TextView that has the name of the horizontal fragment and the vertical fragment position.
The issue that I'm coming across is that when you page horizontally to the end and back to the beginning multiple times, the UI begins to lag. The more that you do this, the more lag occurs and eventually, you are able to page from the beginning to the end without the UI ever updating as it's taking so long. The more you page horizontally, the worse the UI lag becomes.
I've enabled strict mode to see if there's too much work happening on the UI thread, but it isn't. I have also tried to check if LeakCanary can find any memory leaks, but it has yet to find any.
I've just started doing some Tracing via Traceview but as I have just started, I've yet to pinpoint the problem (I'm also not too familiar with Traceview yet, but am working on that).
Here is what the Android Monitor looks like when paging horizontally for a bit and then at the end, paging vertically. You'll notice that paging vertically works fine.
The spikes seen here are color coded as VSync Delay. I have also noticed that if you run the app and let it sit there, the memory will slowly climb until the Garbage Collector is run, which clears some memory and the slow climb will continue again.
My initial thought is that either I'm doing the Loaders wrong or there is an issue with Loaders and nested ViewPagers.
Is there something fundamentally wrong with what I'm attempting here or something wrong with the Loaders, etc.? I am going to continue digging into this, but in the meantime, I was hoping to get some thoughts from others as to what my problem may be or some suggestions into how to pinpoint the issue.
I have created a very basic sample app that can be found at https://github.com/hooked82/MultiDirectionalPaging
If you pull down the project and run it, on first app launch you will need to click the "+" button in the toolbar to populate the database.
It is using the VerticalViewPager from https://github.com/castorflex/VerticalViewPager
UPDATE 8/1/2016
Per Joe's answer, the cause is the amount of inflations and destruction of views when swapping out the adapter of the VerticalViewPager. I was unable to implement swapCursor() on my custom FragmentStatePagerAdapter due to the bug that causes the adapter to not update with new data. If I were to implement swapCursor(), I would get good horizontal paging, but my adapter's UI would not update when the Cursor was notified of dataset changed.
I've swapped out the VerticalViewPager for a custom CursorRecyclerView to get the same functionality, but still need to setup proper flinging of the RecyclerView to mimic that of the VerticalViewPager.
Thanks for the help, Joe!
The issue is the number of fragment transactions that occur on a horizontal scroll.
1) In any view pager, there are at most 3 fragments displayed. For the vertical at position v1 there are 3 fragments v0, v1 and v2. If you scroll from v1 to v2, the v0 fragment is destroyed and v3 is created in anticipation of the next scroll.
So for each vertical scroll a max of two fragment operations may occur.
2) Now the same thing exists horizontally. If at position h1, you have fragments h0, h1, h2 created. Now because the vertical frags also are created you have up to 3 vertical fragments for each horizontal one. Now things get interesting. Assuming you are a v1 in each horizontal fragment the following happens (worse case) scrolling from h1 to h2. First h0 is destroyed, but also the v0, v1, v2 frag owned by h0. Then h3 is created along with at min v0/v1. However if you previously scrolled to v1 it remembers that position and also creates v2.
Therefore for each horizontal scroll a minimum of 6 fragment operations and a max of 8 occur per scroll (ie up to 4x more expensive). When you get into view inflation/cleanup this can multiply really fast causing your performance problem.
You would be better off using a recycler view for at a minimum to handle the vertical scrolling. You likely would need to intercept the scrolling events to "simulate" the snap to page the view pager performs on partial scrolls.
Currently, I am having a ListView with different list item view for each row (Most cases Different).
I am having 6 different item layout, I will add more in future, and I will have only like 5-15 list items, some time may be less and may be many in other cases.
Some item views contains:
ViewPager
ListViews
ImageViews
Texviews
Gridviews
CAROUSEL
Webviews
As this is dynamically generated depends on data, I am facing following issues :
Scrolling Slowly (Some Times)
List Item height
Performance
Is RecyclerView the best solution in this case?
Without seeing your code to identify specific concerns, it's hard to address specific reasons why you are seeing such performance problems. Such as, are you properly using the ViewHolder paradigm? Or are you inappropriately loading stuff on the UI thread when it should be loaded on a background thread? Android has a small section talking about scrolling smoothly with a ListView you should check out. That aside,based on what you have mentioned so far...I think you major problem is design.
Problems
If your ViewPager is using a FragmentPagerAdapter...then that will definitely be causing a lot of overhead and performance drag.
ListView: You should never ever place a ListView within another ListView. This will cause all sorts of problems. Android does not like embedding two scrollable widgets that scroll the same direction. Even if this worked, it'll cause some major performance problems.
GridView: Same goes with the GridView. You should never ever place a GridView within another ListView. Even if this worked, it'll cause some major performance problems.
If you're ImageView is loading some large graphics, it should be on a background thread and not the UI thread. Else you'll get some slow performance
Carousel - I have no idea what API this is but if it scrolls vertically, then it's a no go.
WebViews are a very heavy weight object. I can definitely see this guy slowing things down, especially if it's loading a lot of content.
To build off what #Joffrey has said. There are some major concerns in your choice of UI. Based on what you are placing in this ListView tells me that you need to seriously rethink how to display your content to the user. Eg, try using a TableLayout or GridLayout instead of a GridView.
I am not sure if this is even a question worth asking or I am just bit too much overworked to think through this hence, the community help would surely be great at this stage to resolve this issue.
So, my question is that I have some 20-25 screens in my Android app. The app overall comprises of navigation drawer, tabstrips, fragments and lists in most of those fragments (some fragments only have listviews while some fragments have several other components and listviews as well). Now, few of these listviews have slightly similar row views but, not such that I could use the tag "include" due to placement and size issues.
For example, in rowview_1, I have one imageview (ImageView_1) and three textviews (TextView_1, TextView_2, TextView_3) to its right and finally, an imageview (ImageView_2) on extreme right on whose click an event occurs. Whereas, in rowview_2, I have one imageview (ImageView_1) and three textviews (TextView_1, TextView_2, TextView_3) to its right. On extreme right, two more textviews which convey some extra info and an imageview (ImageView_2) beside ImageView_1 and below TextView_3.
So, what would be a better and optimized approach in such a scenario?
1) Having separate multiple rowview layouts
2) Having one rowview with all the static elements in one place and dynamically adding and removing the extra elements (I believe it could be equally expensive for a rowview in a listview).
I am not sure if there can be any other approach. Any idea/guidance is most welcome.
Thanks in advance!
First of all. I think the differences if there are any are very minimal.
Scenario 1:
1) Cleaner code
2) Easier to make changes to one without affecting the other.
Scenario 2:
1) Complicated layout file
2) Does not necessarily take longer to load if everything's visibility is set to GONE in the layout.
One thing to note is that if you are going to load both layouts in the same Activity, you should surely go with scenario 2. When you load it the first time, the activity caches the layout so the next time, it loads super fast. If they are split over different activity, then the cache is gone anyways. Another thing to consider is how often you make changes to both together. If that is going to be often then scenario 2 is better. Changes will be done in one file. I personally prefer scenario 1. Its easier to show someone else who wants to make changes.
Hope this helps.
I find listView recycles its views too fast.
When my listView scrolls, views falls off the screen gets removed right away.
Each cell(row) has image loaded using universal-image-loader.
Views which fell off the screen has to reload the image when they comes back into visible area. (it shows the stub image for short time period and loads the correct image).
I definately need to keep the view recycling behavior, but can I modify the list view's behavior so that user won't notice constant reloading of images?(maybe I keep 2-3 times of # of views in a cache than a regular list view would)
Unfortunately the code for ListView and friends is horribly complicated by the fact that it's designed to scroll unevenly-sized items without knowing the height ahead of time. That makes it brutally difficult to run with anything but the default behavior. In addition, most of the methods you'd need access to, to easily customize the behavior are hidden or private. It would be a massive job to try and roll your own (across multiple platforms, subtleties of scrolling, flinging, dragging, scrolling, keyboard focus &c).
The best solution is probably to maintain an image cache that fills in lazily around the view positions that are active. Not neccesarily easy. But way easier that trying to mess with ListView.
A very useful API for this is ListView.setRecyclerListener(AbsListView.RecyclerListener listener), which gives you a hook to track which images are actively displayed.
I can suggest you to use a ScrollView instead of the list view. The ListView is designed to display a lot of data efficiently, that's why your off screen items are destroyed. In which concerns the scroll view, once loaded, you will be able to scroll up and down without recreating the off screen objects (because they ill not be destroyed).
See: http://developer.android.com/reference/android/widget/ScrollView.html
EDIT:
If it is mandatory for you to use the listView, you cluld take a look here: How do i prevent recycled ListView items from showing old content?
There is a posibility to override the listView getView method and keep more items not to be destroyed that the number the listView is keeping.
I have looked at this topic to dynamically add views when needed: Add and Remove Views in Android Dynamically?
However, I have a few questions.
1) Will there be a visible stutter or lag when creating these views on the go? For example, I use a panel system where each panel holds a separate view. However, these panels could reach a high number in quantity (40 odd panels?) when the program is complete. In order to preserve resources, I want only the view that is currently visible to actually be created, and the others views to not be instantiated until they are brought into the visible region. I have been told to use a ViewFlipper, but due to animation and user interaction requirements of my program, I cannot use a ViewFlipper, but have something along the same lines that I have created.
For example:
Imagine one of my "panels" to be pretty much like a screen in the Android Home launcher. The thing with the Home Launcher is that all it's views can be kept alive at any given time, because there's only really 7 of them. However, seeing as there are closer to 40 in mine (not all left and right, some above and below as well), I cannot have them all instantiated at the same time, or else the phone will begin to lag.
2) What is the code to destroy the views once they aren't in the visible region?
My program will determine when the panels are in the visible region or not, hence I will only need the code to destroy the views on the go.
Thanks.
Why don't you use a ListView or a GridView?
If you are not willing to use some of those, you will need to do something similar to what they do with the Adapter and how they recicle views.