I'm implementing a ViewPager with tabs similar to implementation provided by Google here.
My app has the following behaviour. My ViewPager has 3 "pages" (fragA, fragB, fragC) and I'm implementing 3 tabs on top of the ViewPager. The differece between google's implementation and mine is that the tabs aren't used to navigate between fragments. As an example, pressing one of my tabs, loads a different set of data on the visible fragment. I can have fragA with tab3 selected, fragB with tab2 selected and fragC with tab1 selected.
The problem is when I rotate the screen. If I'm on FragmentA
with the third tab selected, when I rotate the screen, I stay in the same fragment but now, the first tab is selected. I want to maintain the same tab selected when rotating the screen.
This is my class for managing the tabs:
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, a Fragment (FragmentA, FragmentB and FragmentC) are almost identically:
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
are always called twice and the second time that they are called, the information is lost. I've 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 the 04 call (ManagerTabs -> onViewCreated), mTabHost.setCurrentTab(currentTab);
is called but inside onTabChanged
, I get an java.lang.NullPointerException
because all the fragments inside the adapter are all null.
What can I do so that if I rotate the screen, I can maintain the current selected tab?
After screen rotation new instance of FragmentPagerAdapter
is created. But the ViewPager
restores its state and state of all fragments it contains. ViewPager
doesn't call adapters getView()
method. Therefore Fragment[] currentFragment
contains null
values only.
You can get current fragment by another ways.
If using FragmentPagerAdapter
:
FragmentPagerAdapter
sets tags on fragments it passes to FragmentManager
. This id depends on page index. How this id is generated you can see in source of the latest support lib.
To achieve this just add following method to your ManagerTabs
fragment:
public Fragment getCurrentPagerFragment(int position) {
String fragmentTag = "android:switcher:" + mPager.getId() + ":" + position;
return getChildFragmentManager().findFragmentByTag(fragmentTag);
}
If using FragmentStatePagerAdapter
:
It doesn't sets tags on fragments. With FragmentStatePagerAdapter
it seems we can get by, using its instantiateItem(ViewGroup container, int position)
call. It returns reference to fragment at position position
. If FragmentStatePagerAdapter
already holds reference to fragment in question, instantiateItem
just returns reference to that fragment, and doesn't call getItem()
to instantiate it again. 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 above method to get 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;
}
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With