Search code examples
androidandroid-fragmentsandroid-viewpagerandroid-configchanges

Viewpager on portrait and two panes on landscape


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.

  • How do I make the view pager show my fragments?
  • Is there a better way to achieve the behavior I want?

Solution

  • 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.