简体   繁体   English

如何从savedInstanceState恢复ViewPager

[英]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 . 我在其中一项活动中有一个ViewPager,比如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. 但是当活动尝试恢复其状态时,片段将被恢复,但它们不会附加到viewpager,所以我得到的只是一个白色屏幕。

Here is the relavant code: 这是相关代码:

MainActivity.java 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 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 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: 因此,如果我导航到另一个活动,然后MainActivity在后台被杀死(我使用Do not Keep Activities设置来测试它),会发生这种情况:

1) The Activity gets restored properly, no issues there. 1)活动正确恢复,没有问题。
2) The onCreateView of the fragments are called, with the savedInstanceState intact. 2)调用片段的onCreateView ,且savedInstanceState保持不变。
3) But, the ViewPager and Adapter in my Activity are null . 3)但是,我的Activity中的ViewPager和Adapter是null The fragments dont seem to get attached back to the adapter/viewpager. 片段似乎没有附加到适配器/ viewpager。

So what am I missing here? 那我在这里错过了什么? Am I supposed to do anything to properly restore the fragments? 我应该做些什么来正确恢复碎片吗?

PS: PS:

I am able to solve the issue quickly by replacing this in my MainActivity : 我可以通过在MainActivity替换它来快速解决问题:

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

with just setupViewPager(); 只需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. 所以我不相信这是最好的方法。

By destroying the Activity you also destroy every instance field. 通过销毁Activity您还会销毁每个实例字段。 So you have to re-instantiate the ViewPager and Adapter . 所以你必须重新实例化ViewPagerAdapter There are two scenarios you have to keep in mind. 您必须记住两种情况。

  1. FragmentStatePagerAdapter saves/restores Fragments with the FragmentManager FragmentStatePagerAdapter保存/恢复FragmentsFragmentManager
  2. You have to restore the state of the Fragments inside the Fragment 您必须恢复FragmentsFragment

The first point is the reason why you have two instances of each Fragment . 第一点是每个Fragment有两个实例的原因。 In order to fix this you can get the stored Fragment by its ID . 为了解决这个问题,您可以通过其ID获取存储的Fragment。 See this grepcode FragmentPagerAdapter (same goes for StateAdapter ) 看到这个 grepcode FragmentPagerAdapter (同样适用于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 eg 而不是每次都在setupViewPager中实例化Fragment - 你首先从FragmentManager获取它,例如

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 . 以上是如何从FragmentManager恢复以前保存的Fragments的示例。 You probably have to adjust the cases where you have different types of BaseFragments and so on. 您可能需要调整具有不同类型BaseFragments ,依此类推。 Furthermore you might want to save the Fragment title names and the total count to restore them. 此外,您可能希望保存Fragment标题名称和总计数以还原它们。


The second point is that you have to restore the current Fragment state from the Fragment itself. 第二点是你必须从Fragment本身恢复当前的Fragment状态。 For that your Fragment has the lifecycle methods. 为此,您的Fragment具有生命周期方法。 See this SO topic about that Once for all, how to correctly save instance state of Fragments in back stack? 请参阅此SO主题关于一劳永逸,如何在后端堆栈中正确保存Fragments的实例状态?

FragmentStatePagerAdapter has method saveState() and restoreState() to save and restore it's state.. FragmentStatePagerAdapter具有saveState()和restoreState()方法来保存和恢复它的状态。

https://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html#saveState() https://developer.android.com/reference/android/support/v4/app/FragmentStatePagerAdapter.html#saveState()

saveState: Save any instance state associated with this adapter and its pages that should be restored if the current UI state needs to be reconstructed. saveState:保存与此适配器关联的任何实例状态及其在需要重建当前UI状态时应恢复的页面。

So it seems that it could be possible to use these methods to archive your goal. 因此,似乎可以使用这些方法来存档您的目标。 I'm sorry, but I don't have compiler now, so I couldn't tested code below. 对不起,我现在没有编译器,所以我无法测试下面的代码。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

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

private void setupViewPager(Bundle savedInstanceState) {
    mAdapter = new ViewPagerAdapter(getSupportFragmentManager());

    if (savedInstanceState != null) {
        Parcelable parcelable = savedInstanceState.getParcelable("Adapter");
        mAdapter.restoreState(parcelable, getClassLoader());
    } else {
        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);
}

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);

    Parcelable parcelable = mAdapter.saveState();
    outState.putParcelable("Adapter", parcelable);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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