简体   繁体   中英

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?

Have you tried using PagerAdapter instead of FragmentPagerAdapter? You will get better control over creation and handling of fragments. Otherwise you dont know what is the FragmentPagerAdapter doing (hes doing it for you, but probably wrong).

You cam move much of that logic into that class making the activity cleaner, and probably also fix the problems you have.

EDIT: Since we are talking orientation changes here, you should probably incorporate onConfigurationChanged callback to handle that logic better.

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.

Try this

Portrait Xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.design.widget.TabLayout
        android:id="@+id/tab"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

    <android.support.v4.view.ViewPager
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>

Landscape Xml:

 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">
    <FrameLayout
        android:id="@+id/fragmentA"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
    <FrameLayout
        android:id="@+id/fragmentB"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1" />
</LinearLayout>

Activity Java:

package com.dev4solutions.myapplication;

import android.graphics.Point;
import android.os.Bundle;
import android.support.design.widget.TabLayout;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;

import com.dev4solutions.myapplication.fragment.FragmentA;
import com.dev4solutions.myapplication.fragment.FragmentB;

public class FragmentActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Point point = new Point();
        getWindowManager().getDefaultDisplay().getSize(point);
        setContentView(R.layout.activity_frag);
        if (point.y > point.x) {
            TabLayout tabLayout = findViewById(R.id.tab);
            ViewPager viewPager = findViewById(R.id.pager);
            tabLayout.setupWithViewPager(viewPager);
            viewPager.setAdapter(new PagerAdapter(getSupportFragmentManager()));

        } else {
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.fragmentA, new FragmentA())
                    .commit();
            getSupportFragmentManager()
                    .beginTransaction()
                    .add(R.id.fragmentB, new FragmentB())
                    .commit();
        }
    }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM