Android ViewPager with tab not supporting state after screen rotation

I am implementing a ViewPager with tabs similar to the implementation provided by Google here .

My application has the following behavior. My ViewPager has 3 "pages" (fragA, fragB, fragC), and I implement 3 tabs on top of the ViewPager. The difference between google and mine is that tabs are not used to move between fragments. For example, clicking one of my tabs loads another data set into the visible fragment. I can have fragA with tab3 selected, fragB with tab2 selected and fragC with tab1 selected.

The problem is that I rotate the screen. If I am on FragmentA with the third tab selected, when I rotate the screen, I remain in the same fragment, but now the first tab is selected. I want to keep the same tab that was selected when the screen was rotated.

This is my tab management class:

 public class ManagerTabs extends Fragment { private TabHost.TabContentFactory mFactory = new TabHost.TabContentFactory() { @Override public View createTabContent(String tag) { View v = new View(getActivity()); v.setMinimumWidth(0); v.setMinimumHeight(0); return v; } }; public static ManagerTabs newManager() { return new ManagerTabs(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = inflater.inflate(R.layout.datatabs, container, false); createTabs(); createPager(); return view; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (savedInstanceState != null) { currentPage = savedInstanceState.getInt("currentPage", currentPage); currentTab = savedInstanceState.getInt("currentTab", currentTab); mTabHost.setCurrentTab(currentTab); } mPager.setCurrentItem(currentPage); } private void createPager() { mPager = (ViewPager) view.findViewById(R.id.datapager); pagerAdapter = new PagerAdapter(getChildFragmentManager()); mPager.setAdapter(pagerAdapter); IconPageIndicator indicator = (IconPageIndicator) rootView.findViewById(R.id.indicator); indicator.setViewPager(pager); indicator.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { @Override public void onPageSelected(int position) { currentTab = mTabHost.getCurrentTab(); currentPage = mPager.getCurrentItem(); switch (currentPage) { case 0: ((FragmentA) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); break; case 1: ((FragmentB) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); break; case 2: ((FragmentC) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); break; } } }); } private void createTabs() { mTabHost = (TabHost) view.findViewById(android.R.id.tabhost); mTabHost.setup(); mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.day))setIndicator(getString(R.string.day)).setContent(mFactory)); mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.month)).setIndicator(getString(R.string.month)).setContent(mFactory)); mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.year)).setIndicator(getString(R.string.year)).setContent(mFactory)); mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() { public void onTabChanged(String tag) { currentTab = mTabHost.getCurrentTab(); currentPage = mPager.getCurrentItem(); switch (currentPage) { case 0: ((FragmentA) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); break; case 1: ((FragmentB) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); break; case 2: ((FragmentC) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab); break; } } }); } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("currentPage", mPager.getCurrentItem()); outState.putInt("currentTab", mTabHost.getCurrentTab()); } } 

And this is the adapter that creates the fragments:

 public class PagerAdapter extends FragmentPagerAdapter implements IconPagerAdapter{ private Fragment[] currentFragment = new Fragment[3]; public PagerAdapter(FragmentManager fm) { super(fm); } @Override public Fragment getItem(int index) { Fragment fragment; switch (index) { case 0: fragment = FragmentA.newInstance(); break; case 1: fragment = FragmentB.newInstance(); break; case 2: fragment = FragmentC.newInstance(); break; default: fragment = null; break; } currentFragment[index] = fragment; return fragment; } @Override public int getIconResId(int index) { switch (index) { case 0: return R.drawable.fraga; case 1: return R.drawable.fragb; case 2: return R.drawable.fragc; default: return -1; } } @Override public int getCount() { return 3; } public Fragment getCurrentFragment(int currentFrag) { return currentFragment[currentFrag]; } public Fragment[] getCurrentFragment() { return currentFragment; } } 

And finally, the fragment (FragmentA, FragmentB and FragmentC) is almost identical:

 public class FragmentA extends Fragment { public static FragmentA newInstance() { return new FragmentA(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { rootView = inflater.inflate(R.layout.datafragment, container, false); mTabHost = (TabHost) ((View) container.getParent().getParent()).findViewById(android.R.id.tabhost); if (savedInstanceState != null) { currentTab = savedInstanceState.getInt("currentTab"); } populateLayout(currentTab); return rootView; } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("currentTab", currentTab); } } 

The problem is that all my onCreateView always called twice, and the second time they are called, the information is lost. I debugged and saw this behavior,

When I rotate the screen, for example, in FragmentA and with the third tab selected, this happens:

 01 ManagerTabs -> onSaveInstanceState 02 FragmentA -> onSaveInstanceState 03 ManagerTabs -> onCreateView 04 ManagerTabs -> onViewCreated 

In a call 04 (ManagerTabs -> onViewCreated) mTabHost.setCurrentTab(currentTab); is called mTabHost.setCurrentTab(currentTab); but inside onTabChanged , I get java.lang.NullPointerException because all the fragments inside the adapter are null.

What can I do so that if I rotate the screen, I can save the currently selected tab?

+6
source share
1 answer

After the screen rotates, a new instance of the FragmentPagerAdapter . But ViewPager restores the state and state of all fragments that it contains. ViewPager does not call getView() adapters. Therefore, Fragment[] currentFragment contains only null values.

You can get the current fragment in other ways.

When using the FragmentPagerAdapter :

FragmentPagerAdapter sets tags to the fragments that it passes to the FragmentManager . This identifier depends on the page index. How this identifier is generated, you can see in the source of the latest version of support .

To achieve this, simply add the following method to your ManagerTabs :

 public Fragment getCurrentPagerFragment(int position) { String fragmentTag = "android:switcher:" + mPager.getId() + ":" + position; return getChildFragmentManager().findFragmentByTag(fragmentTag); } 

When using the FragmentStatePagerAdapter :

It does not set labels on fragments. With the FragmentStatePagerAdapter it seems we can get through using its call instantiateItem(ViewGroup container, int position) . It returns a link to the fragment at position . If the FragmentStatePagerAdapter already contains a link to the fragment in question, instantiateItem simply returns a link to this fragment and does not call getItem() to getItem() it. In this case, our method will be:

 public Fragment getCurrentPagerFragment(int position) { FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) mPager.getAdapter(); return (Fragment) a.instantiateItem(pager, position); } 

Finally:

Now use the above method to get the current fragment. In your case:

 mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() { public void onTabChanged(String tag) { currentTab = mTabHost.getCurrentTab(); currentPage = mPager.getCurrentItem(); Fragment currentFragment = getCurrentPagerFragment(currentPage); switch (currentPage) { case 0: ((FragmentA) currentFragment).getData(currentTab); break; case 1: ((FragmentB) currentFragment).getData(currentTab); break; case 2: ((FragmentC) currentFragment).getData(currentTab); break; } } }); 
+13
source

Source: https://habr.com/ru/post/980075/


All Articles