Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ViewPager inside fragment, how to retain state?

In my application the fragment activity holds two fragments, Fragment A and Fragment B. Fragment B is a view pager that contains 3 fragments.

enter image description here

In my activity, to prevent that the fragment is recreated on config changes:

if(getSupportFragmentManager().findFragmentByTag(MAIN_TAB_FRAGMENT) == null) {
    getSupportFragmentManager().beginTransaction().replace(R.id.container, new MainTabFragment(), MAIN_TAB_FRAGMENT).commit();
}

Code for Fragment B:

public class MainTabFragment extends Fragment {

    private PagerSlidingTabStrip mSlidingTabLayout;
    private LfPagerAdapter adapter;
    private ViewPager mViewPager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_tab, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {

        this.adapter = new LfPagerAdapter(getChildFragmentManager());

        this.mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
        this.mViewPager.setAdapter(adapter);

        this.mSlidingTabLayout = (PagerSlidingTabStrip) view.findViewById(R.id.sliding_tabs);
        this.mSlidingTabLayout.setViewPager(this.mViewPager);
    }
}

Code for the adapter:

public class LfPagerAdapter extends FragmentPagerAdapter {

    private static final int NUM_ITEMS = 3;

    private FragmentManager fragmentManager;

    public LfPagerAdapter(FragmentManager fm) {
        super(fm);
        this.fragmentManager = fm;
    }

    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public Fragment getItem(int position) {
        Log.d("TEST","TEST");
        switch (position) {
            case 1:
                return FragmentC.newInstance();
            case 2:
                return FragmentD.newInstance();
            default:
                return FragmentE.newInstance();
        }
    }
}

My problem is that I am not able to retain the state of the view pager an its child fragments on orientation changes.

Obviously this is called on every rotation:

this.adapter = new LfPagerAdapter(getChildFragmentManager());

which will cause the whole pager to be recreated, right? As a result

getItem(int position)

will be called on every rotation and the fragment will be created from scratch and losing his state:

return FragmentC.newInstance();

I tried solving this with:

if(this.adapter == null)
    this.adapter = new LfPagerAdapter(getChildFragmentManager());

in onViewCreated but the result was that on rotation the fragments inside the pager where removed.

Any ideas how to correctly retain the state inside the pager?

like image 657
UpCat Avatar asked Nov 18 '14 12:11

UpCat


People also ask

Is ViewPager deprecated?

Technically, ViewPager is not deprecated, but the two concrete PagerAdapter implementations — FragmentPagerAdapter and FragmentStatePagerAdapter — are deprecated.

How do I use Tablayout with ViewPager?

Tab layout are visible below toolbar with View pager, used to create swipeable views . Tabs are designed to work with fragments. Use them to swipe fragments in view pager.

How do you make a ViewPager only resume selected fragment?

1 Answer. Show activity on this post. You cannot do that, the viewpager requires at least one fragment to the left and one to the right. I suggest you move the onResume() logic to a separate method and call it when the fragment becomes visible.

What is difference between ViewPager and ViewPager2?

ViewPager2 is an improved version of the ViewPager library that offers enhanced functionality and addresses common difficulties with using ViewPager . If your app already uses ViewPager , read this page to learn more about migrating to ViewPager2 .

What is a viewpager layout?

Layout that allows the user to swipe left and right through "pages" of content which are usually different fragments. This is a common navigation mode to use instead of ActionBar Tabs with Fragments. A ViewPager is a layout which can be added to any layout XML file inside a root layout:

How do I save and restore the VIEWSTATE of a fragment?

As mentioned in table 1, views save and restore their ViewState through all operations that don't remove the fragment or destroy the host. Your fragment is responsible for managing small amounts of dynamic state that are integral to how the fragment functions. You can retain easily-serialized data using Fragment.onSaveInstanceState (Bundle) .

What is the use of fragmentmanager in Android?

A FragmentManager manages Fragments in Android, specifically, it handles transactions between fragments. A transaction is a way to add, replace, or remove fragments. getPageTitle (int pos): (optional) Similar to getItem () this methods returns the title of the page at index pos.

How to access the selected page within the viewpager at runtime?

We can access the selected page within the ViewPager at any time with the getCurrentItem method which returns the current page: The current page can also be changed programmatically with the With this getter and setter, we can easily access or modify the selected page at runtime.


2 Answers

You will need to do 2 things to resolve the issue:

1) You should use onCreate method instead of onViewCreated to instantiate LfPagerAdapter;

i.e.:

    public class MainTabFragment extends Fragment {

    private PagerSlidingTabStrip mSlidingTabLayout;
    private LfPagerAdapter adapter;
    private ViewPager mViewPager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        this.adapter = new LfPagerAdapter(getChildFragmentManager());
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_tab, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {


        this.mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
        this.mViewPager.setAdapter(adapter);

        this.mSlidingTabLayout = (PagerSlidingTabStrip) view.findViewById(R.id.sliding_tabs);
        this.mSlidingTabLayout.setViewPager(this.mViewPager);
    }
}

2) You will need to extend FragmentStatePagerAdapter instead of FragmentPagerAdapter

like image 181
Mehul Joisar Avatar answered Oct 12 '22 01:10

Mehul Joisar


Android will automatically recreate your activity on configuration without this line android:configChanges="keyboardHidden|orientation|screenSize" in you manifest so that you can handle onConfiguration change yourself.

The only way then is to use onSaveInstanceState() in both your activityFragement to save viewPager state(current position for example) and in fragments where you need to save stuff

Example on how you can save current position of viewpager and restore it onConfiguration change

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
            int position = mViewPager.getCurrentItem();
            outState.Int("Key", position );
    }

    @Override //then restore in on onCreate();
    public void onCreated(Bundle savedInstanceState) {
        super.onCreated(savedInstanceState);
        // do stuff
        if (savedInstanceState != null) {
              int  position= savedInstanceState.getInt("Key");
            mViewPager.setCurrentItem(position)
        }
    }

Of course, this is a very basic example.

Ps: to restore in fragment use onActivityCreated() instead of onCreate() method.

Here is another example on how to retain state : Click me!

like image 1
murielK Avatar answered Oct 12 '22 00:10

murielK