I'm trying to achieve a layout that shows a view pager when the device is shown on portrait and show two panes when device is on landscape.
So I made two different layout files, one with only a ViewPager, the other with a LinearLayout and the other with two FrameLayouts, I don't think it is necessary to show them here. There is also a boolean value hasTwoPanes
for the two configurations.
@Inject FragmentOne fragmentOne;
@Inject FragmentTwo fragmentTwo;
@Override
protected void onCreate(Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_main);
FragmentManager fm = getSupportFragmentManager();
boolean hasTwoPanes = getResources().getBoolean(R.bool.hasTwoPanes);
TabLayout tabLayout = findViewById(R.id.tab_layout);
ViewPager viewPager = findViewById(R.id.view_pager);
if (hasTwoPanes) {
tabLayout.setVisibility(View.GONE);
} else {
tabLayout.setVisibility(View.VISIBLE);
tabLayout.setupWithViewPager(viewPager);
viewPager.setAdapter(new MyPagerAdapter(fm));
}
FragmentOne frag1 = (FragmentOne) fm.findFragmentByTag(getFragmentName(0));
if (frag1 != null) fragmentOne = frag1;
FragmentTwo frag2 = (FragmentTwo) fm.findFragmentByTag(getFragmentName(1));
if (frag2 != null) fragmentTwo = frag2;
if (hasTwoPanes) {
if (frag1 != null) {
fm.beginTransaction().remove(fragmentOne).commit();
fm.beginTransaction().remove(fragmentTwo).commit();
fm.executePendingTransactions();
}
fm.beginTransaction().add(R.id.frame_frag1, fragmentOne, getFragmentName(0)).commit();
fm.beginTransaction().add(R.id.frame_frag2, fragmentTwo, getFragmentName(1)).commit();
}
}
private class MyPagerAdapter extends FragmentPagerAdapter {
MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
if (position == 0) {
return fragmentOne;
} else {
return fragmentTwo;
}
}
@Override
public int getCount() {
return 2;
}
}
private static String getFragmentName(int pos) {
return "android:switcher:" + R.id.view_pager + ":" + pos;
}
Two fragments are injected with Dagger. If no fragments were already present, these injected fragments are added to the view pager or the layout depending on the orientation.
Because the view pager adapter gives a name to its fragments, I need to know that name (hence getFragmentName(int pos)
method) to get back that fragment after rotation.
The result is state is correctly restored when rotating from portrait to landscape, but when rotating from landscape the portrait, the view pager is completely empty. When I rotate back to landscape, the fragments reappear. The tab layout is also buggy, there is no swipe animation, I can just continuously slide from one tab to the other, stopping anywhere.
To clarify things, this is happening in an Activity, there is no parent fragment. Even though the fragments are not shown, the fragment's onViewCreated
is called. The view pager seems to correctly restore the fragment references in instantiateItem
. Also, when debugging, fragments have the added
to true and hidden
to false. This make it seem like a view pager rendering issue.
The solution I found is very simple, override getPageWidth
in my pager adapter like this:
@Override
public float getPageWidth(int position) {
boolean hasTwoPanes = getResources().getBoolean(R.bool.hasTwoPanes);
return hasTwoPanes ? 0.5f : 1.0f;
}
R.bool.hasTwoPanes
being a boolean resources available in default configuration and values-land
. My layout is the same for both configuration, and the tab layout is simply hidden on landscape.
The fragment restoration is done automatically by the view pager.