I have an application using two tabs, both containing fragments. I am using the FragmentPagerAdapter to manage tab changes. All works fine with two tabs.
I recently added a third tab and am having a bit of trouble. tab1(fragment1) is a LinearLayout of small fragments. tab2(fragment2) is a simple layout of text. tab3(fragment3) is also a simple layout of text.
If I switch between tab1 and tab2 both work properly and retain their states. However, if I switch between tab1 and tab3, tab3 shows the text properly but tab1 shows a blank tab.
I know that if a tab goes more than 1 position off the current position the fragment will be destroyed and the fragment will need to be recreated. Does FragmentPagerAdapter not do this automatically?
I discovered that if I rotate the device (with tab1 selected), the tab1 fragment will be restored with its correct state so the fragment is not being destroyed. It seems like there is an issue with the layout not being correctly recreated by the ViewPager but this is only a guess.
As a work around I set myViewPager.setOffscreenPageLimit(2) and the layout is retained. I would like to get this to work as I think its supposed to without forcing the fragments to stay in memory.
Reading docs for setOffScreenPageLimit(), makes me think your approach is fine. Look at this:
If you have a small number of pages (3-4) that you can keep active all
at once, less time will be spent in layout for newly created view
subtrees as the user pages back and forth.
You should keep this limit low, especially if your pages have complex
layouts.
I'm curious though if setRetainInstance(true) will work, since it's the Adapter who decides to recreate a fragment not that it's get recreated during some Android change config operation.
Add
setRetainInstance(true);
in onCreateView method of a Fragment
Related
Without me pasting any code, i have an activity that shows three fragments using a viewpager to swipe between them.
When the activity is created, tab one is showing.. If i select the second tab and go back to the first tab, everything is fine. But if i select the third tab and then select the first tab again, the onResume method is called within the first tab and its causing some undesirable results... why would onResume only be called when coming from the third tab and not the second? Its like tab one is being killed when you swipe to tab three, but tab one is not being killed if you just swipe to tab two..
Without seeing the code I can only guess, but when you use tab you are going to be using a FragmentPagerAdapter or FragmentPagerStateAdapter this keeps reference to the fragments that are kept alive. I'm going to guess that you are using the FragmentPagerStateAdapter which only keeps certain fragments alive and destroys other fragments when they leave the view. So that when you are on the 3rd fragment, the first fragment is being destroyed, which is recreated and thus calls onResume when you go back and visit it. When you are on the 2nd tab, the fragment isn't destroyed and so when you visit it, it does not need to call onResume. Fragments keep one fragment to the left and one to the right "alive" even though they are not in view. So when you are on 1, 2 is alive. When you are on 2, 1 & 3 are alive, when you are on 3, 2 is alive. I hope this helps.
To add more to BlackHat's answer,
You should use FragmentStatePagerAdapter, if you dont already, instead of FragmentPagerAdapter.
Also you should set the OffscreenPageLimit property of yoru view pager to the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state.
I use a viewpager with a tablayout in which I add some fragments with an adapter.
In my fragments oncreateview and onviewcreated are called.
My question is : is a fragment created again when the user scrolls to the next fragment of the tablayout and comes back to the first fragment? Or the fragments are created at the initialization of the viewpager and tablayout and then don't change?
I ask because I want to change a value from a tablayout's fragment and that change must entail a change in another fragment of the tablayout.
If my question is not clear, tell me and I will try to explain better.
There is a property called setOffscreenPageLimit in ViewPager.
You can set the number of pages that should be retained to either side of the current page in the view hierarchy in an idle state. Pages beyond this limit will be recreated from the adapter when needed.
viewpager.setOffscreenPageLimit(X)
it means it will create X number of pageson either side of your current page. And once it it is out of your page range it will recreate it.
By default 1 page beside the current page is kept "in memory" and not recreated. You can change that through the following line:
viewPager.setOffscreenPageLimit(YOUR_LIMIT);
It is suggested that you keep this LIMIT low, you can read about it here.
The Fragments that are within your specified limit will be kept "in memory" and will not be recreated. However Fragments that are beyond this limit will be recreated.
I am trying to read RSS feed and show the content of it in card view which is inside ViewPager with tab layout. It shows fragment with data initially, but when swiped back from another tab, the whole card layout with data,disappears.
I had to handle the same situation. I had TabLayout in ViewPager.
I have used FragmentPagerAdapter with off screen page limit set to (NO_OF_TABS - 1). This is to hold all the fragments in memory and they wont be destroyed and recreated as you swipe back and forth. As my fragments are small in size meaning not many views every fragment has so it was okay for me. But this way fragments always holds the latest data. Say, if user had entered text in EditText and swipe to end and come back, then fragment will still have the text that user had entered.
In your case, if you have not set the page limit the default is 1 so the fragment gets destroyed when you swipe away so you lose the data.
IN ADDITION TO ABOVE:
If you can not keep all fragments in memory, you may consider using FragmentStatePagerAdapter. This adapter saves the states of fragments that are being destroyed and it will use the same state to represent in fragment when user swipe back to this fragment. But there are issues around StaePagerAdapter while it destroys adn recreate the fragments, there are indexing issues so you will easily get IndexOutOfBoundException if you swipe. This is with page screen limit set to less than the total fragments.
Am running in some weird problem. I have implemented ViewPagerTabStrip by following the tutorial Here
It's working fine and i have implemented Three Tabs.
On the first Page /tab , i am loading the data from server & displaying it in the ListView
The problem is, when i swipe to the Third Page & then i swipe back to Second page, the data of the first page is being loaded from the server again.( View of first tab is re-created.)
From the logs, it seems that this is the behavior of ViewPager & Adapter.
When we are on the first tab, it creates the view of second tab/ page as well in background & when we are on the Second tab, it was doing same for Third tab in background & upon reaching to third tab, it didn't created the view for it self(i.e third tab).
But when we swipe back to second tab, it re created the view of first tab as well.
How to solve this ? we can't afford to load the data from server again and again while swiping through the tabs.
I mean is there any way to stop the reload of data (or re-creation of view) while just swiping through tab pages? It should load the data when the whole fragment activity of View Pager is created and not at the time of swiping.
Any help on this please ?
Yeah, the ViewPager only keeps a certain number of fragments in memory before destroying them- this default number is usually two. Thus, what you have to do is tell the ViewPager to retain those fragments.
The simplest solution is to simply tell the ViewPager to keep three fragments in memory as follows:
viewPager.setOffscreenPageLimit(3);
Here are a few suggested answers on how to retain fragments (with more than just this method) and the reasoning behind the default behaviour as well as these solutions:
ViewPager and fragments — what's the right way to store fragment's state?
retain state of viewpager fragments android
I have created an android app that uses a ViewPager to swipe through three fragments (each of the three fragments contains a gridview).
I began learning about fragments recently and assumed that after my gridviews were created in their fragments, that each fragment would be in memory and never have to load again.
Recently I noticed that when I swiped from my left fragment, to the middle fragment, then to the right fragment, that the left-most fragment would have to have its gridview filled again with my adapter.
I was just wondering why this happens when I navigate from the left-most fragment to the right-most fragment, but not when I navigate between side-by-side fragments. Does it only keep the most recent fragment in memory and kill the other fragment? Or is there some other reason why an app wouldn't keep all three fragments in memory? My app gets a little laggy and slow when I quickly navigate between fragments, so it would be nice to only have to draw each gridview one time if possible.
I fill my arraylist (used to fill adapter) in onCreate(), and then fill my gridview with the adapter in onActivityCreated(Bundle savedInstanceState)
The ViewPager keeps a certain amount of off-screen tabs (Fragments, of course) in memory. The default, I believe for all devices, is 1. Thus, when you scroll to the far right, only the one to its left will be kept in memory.
If you want your ViewPager to retain all tabs in memory (careful--this can be hard on the device or other running apps), you can set how many off-screen tabs to keep in memory. To do this, use the setOffscreenPageLimit method of ViewPager on your ViewPager object.
Example:
ViewPager pager = (ViewPager) findViewById(R.id.pager); // Your pager's ID
pager.setOffscreenPageLimit(2); // Will retain 2 off-screen tabs + your selected tab
Hope this helps!
It's the nature of how a FragmentPagerAdapter and FragmentPagerStateAdapter work. From Google's site:
The fragment of each page the user visits will be kept in memory, though its view hierarchy may be destroyed when not visible.
Meaning, your GridView is refilling because your ViewPager killed the view and has to rebuild it.
EDIT: If you need to keep all three fragment Views in memory to speed things up, then you would have to create your own PagerAdapter that stores the views in instantiateItem(ViewGroup, int) inside an Collection of some sort.
Alternatively, if you are using Google's example verbatim, then you probably are doing something like this:
#Override
public Fragment getItem(int position) {
return ArrayListFragment.newInstance(position);
}
In which case, you're rebuilding a new fragment each time the ViewPager requests one.
By default ViewPager keeps in an idle state one fragment from the left and one from the right of the current page. You can change this behaviour with ViewPager.setOffscreenPageLimit(). See docs