Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to Restore ViewPager from savedInstanceState

So I have a problem that I can't seem to solve. I have a ViewPager in one of my activities, say MainActivity. I am implementing all the required methods to save and retrieve instance state when the activity gets killed in the background. But when the activity tries to restore its state, the fragments are restored but they are not attached to the viewpager so all I get is a white screen.

Here is the relavant code:

MainActivity.java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    //..
    if (savedInstanceState==null) {
        setupViewPager();
    }
}

private void setupViewPager() {
    mAdapter = new ViewPagerAdapter(getSupportFragmentManager());
    mAdapter.addFrag(FirstFragment.newInstance(mFeedItem), "INFO");
    //Note: mFeedItem is obtained from the intent and I have verified
    //      that it is properly restored.
    mAdapter.addFrag(SecondFragment.newInstance(mFeedItem), "SERVICES");
    mViewPager.setAdapter(mAdapter);
    mTabLayout.setupWithViewPager(mViewPager);
}

ViewPagerAdapter.java

public class ViewPagerAdapter extends FragmentStatePagerAdapter {
    private final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position);
    }

    @Override
    public int getCount() {
        return mFragmentList.size();
    }

    public void addFrag(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return mFragmentTitleList.get(position);
    }
}

FirstFragment.java

public static FirstFragment newInstance(Workshop item) {
    FirstFragment f = new FirstFragment();
    f.mItem = item;
    return f;
}

@Override
public void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putString("feed_item",Workshop.packToGson(mItem));
}

@Nullable
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragment_item_detail_info, container, false);
    if (savedInstanceState!=null) {
        mItem = Workshop.extractGson(savedInstanceState.getString("feed_item"));
    }
    return v;
}

So if I navigate to another activity and then the MainActivity gets killed in the background ( I am using the Don’t Keep Activities setting to test this), this happens:

1) The Activity gets restored properly, no issues there.
2) The onCreateView of the fragments are called, with the savedInstanceState intact.
3) But, the ViewPager and Adapter in my Activity are null. The fragments dont seem to get attached back to the adapter/viewpager.

So what am I missing here? Am I supposed to do anything to properly restore the fragments?

PS:

I am able to solve the issue quickly by replacing this in my MainActivity:

if (savedInstanceState==null) {
    setupViewPager();
}

with just setupViewPager();, so that a new adapter and fragments are created irrespective of whether it is restoring or launching for the first time. But that comes with the drawback of resetting the state of the fragments. Also, when I do that, I find that there are two instances of each fragment, one which is restored and a new one, with only the new instances actually displayed. So I dont believe that it is the best approach.

like image 512
ShahiM Avatar asked Nov 04 '17 07:11

ShahiM


People also ask

How do I get ViewPager?

You can create swipe views using AndroidX's ViewPager widget. To use ViewPager and tabs, you need to add a dependency on ViewPager and on Material Components to your project. To insert child views that represent each page, you need to hook this layout to a PagerAdapter .

Is ViewPager deprecated?

This function is deprecated.

Can we add activity in ViewPager?

You can't use ViewPager to swipe between Activities . You need to convert each of you five Activities into Fragments , then combine everything in one FragmentActivity with the Adapter you use with ViewPager .

How do I update my Android ViewPager?

Firstly set viewpager adapter to null then recreate the adapter and set i to it to viewpager. Show activity on this post. Define which message which is needed to update. Write below code in the Fragment which is needed update.


1 Answers

By destroying the Activity you also destroy every instance field. So you have to re-instantiate the ViewPager and Adapter. There are two scenarios you have to keep in mind.

  1. FragmentStatePagerAdapter saves/restores Fragments with the FragmentManager
  2. You have to restore the state of the Fragments inside the Fragment

The first point is the reason why you have two instances of each Fragment. In order to fix this you can get the stored Fragment by its ID. See this grepcode FragmentPagerAdapter (same goes for StateAdapter)

private static String makeFragmentName(int viewId, int index) {
    return "android:switcher:" + viewId + ":" + index;
}

Instead of instantiating a Fragment in setupViewPager every time - you get it from the FragmentManager first e.g.

mAdapter = new ViewPagerAdapter(getSupportFragmentManager());
int fragmentCount = 2; // save value if it's a arbitrary list of fragments
for (int index = 0; i < fragmentCount; i++) {
    FirstFragment firstFragment = (FirstFragment) getSupportFragmentManager().findFragmentByTag(makeFragmentName(R.id.view_pager, index));
    if (firstFragment == null) {
        firstFragment = FirstFragment.newInstance(mFeedItem);
    }
    mAdapter.addFrag(firstFragment, "someTitle");
}

The above is an example on how to restore previous saved Fragments from the FragmentManager. You probably have to adjust the cases where you have different types of BaseFragments and so on. Furthermore you might want to save the Fragment title names and the total count to restore them.


The second point is that you have to restore the current Fragment state from the Fragment itself. For that your Fragment has the lifecycle methods. See this SO topic about that Once for all, how to correctly save instance state of Fragments in back stack?

like image 81
Murat Karagöz Avatar answered Oct 05 '22 02:10

Murat Karagöz