【问题标题】:How to Restore ViewPager from savedInstanceState如何从 savedInstanceState 恢复 ViewPager
【发布时间】:2018-04-16 22:28:47
【问题描述】:

所以我有一个我似乎无法解决的问题。我的一项活动中有一个 ViewPager,比如 MainActivity。当活动在后台被杀死时,我正在实现所有必需的方法来保存和检索实例状态。但是当 Activity 尝试恢复其状态时,片段会被恢复,但它们没有附加到 viewpager,所以我得到的只是一个白屏。

这里是相关代码:

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;
}

因此,如果我导航到另一个活动,然后 MainActivity 在后台被杀死(我正在使用 Don’t Keep Activities 设置来测试这一点),就会发生这种情况:

1) Activity 已正确恢复,没有问题。
2)片段的onCreateView被调用,savedInstanceState完好无损。
3) 但是,我的 Activity 中的 ViewPager 和 Adapter 是 null。片段似乎没有连接回适配器/viewpager。

那么我在这里错过了什么?我应该做些什么来正确恢复碎片吗?

PS:

我可以通过在我的MainActivity 中替换它来快速解决问题:

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

只使用setupViewPager();,以便创建新的适配器和片段,无论它是第一次恢复还是启动。但这带来了重置片段状态的缺点。另外,当我这样做时,我发现每个片段有两个实例,一个是恢复的,一个是新的,实际上只显示了新的实例。所以我不认为这是最好的方法。

【问题讨论】:

    标签: java android performance android-fragments android-viewpager


    【解决方案1】:

    通过销毁Activity,您还可以销毁每个实例字段。所以你必须重新实例化ViewPagerAdapter。您必须牢记两种情况。

    1. FragmentStatePagerAdapter 使用 FragmentManager 保存/恢复 Fragments
    2. 您必须在Fragment 中恢复Fragments 的状态

    第一点是每个Fragment 都有两个实例的原因。为了解决这个问题,您可以通过 ID 获取存储的 Fragment。 See this grepcode FragmentPagerAdapterStateAdapter 也是如此)

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

    不是每次都在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");
    }
    

    以上是如何从FragmentManager 恢复之前保存的Fragments 的示例。您可能必须调整您拥有不同类型的BaseFragments 等情况。此外,您可能希望保存 Fragment 标题名称和恢复它们的总数。


    第二点是你必须从Fragment本身恢复当前的Fragment状态。为此,您的 Fragment 具有生命周期方法。看到这个关于那个Once for all, how to correctly save instance state of Fragments in back stack?的SO主题

    【讨论】:

    • 我试过了,但findFragmentByTag 调用始终为空
    • @ShahiM 如果你做对了,那意味着Fragment 一开始就没有被实例化/保存。然后你必须使用if-branch 来实例化一个新的。
    • 似乎不适用于FragmentStatePagerAdapter。我把它改成了FragmentPagerAdapterand all iz well
    • 我遵循了所有这些,因为我遇到了同样的问题。它在将 FragmenStatePagerAdapter 更改为 FragmentPagerAdapter 后起作用,除了添加到 ViewPager 的 Fragment 是不可见的,就像它没有膨胀一样,即使调用 onViewCreated 也是如此。我不明白为什么。
    【解决方案2】:

    FragmentStatePagerAdapter 有方法 saveState() 和 restoreState() 来保存和恢复它的状态..

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

    saveState:保存与此适配器及其页面关联的任何实例状态,如果需要重建当前 UI 状态,则应恢复这些状态。

    因此,似乎可以使用这些方法来归档您的目标。对不起,我现在没有编译器,所以我无法测试下面的代码。

    @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);
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-06-22
      • 2016-06-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多