当恢复时,ViewPager中的Fragment视图未被恢复

38

我已设置了ActionBar选项卡,其中包括4个选项卡。一切正常,直到我从TabbedFragment导航离开并返回。

我是这样创建选项卡的:

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

        final ActionBar actionBar = getActionBar();

        tabs = Lists.newArrayList();
        tabs.add(new TabDefinition<>("Tab 1"));
        tabs.add(new TabDefinition<>("Tab 2"));
        tabs.add(new TabDefinition<>("Tab 3"));
        tabs.add(new TabDefinition<>("Tab 4"));


        for (TabDefinition tab : tabs) {
            actionBar.addTab(actionBar.newTab()
                .setText(tab.text)
                .setTag(tab.tag)
                .setTabListener(this));
        }
    }

可以这样初始化适配器:

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

@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    viewPager = (ViewPager) view.findViewById(R.id.pager);

    viewPager.setAdapter(new FragmentStatePagerAdapter(getFragmentManager()) {

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

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

    viewPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
        @Override
        public void onPageSelected(int position) {
            getActionBar().setSelectedNavigationItem(position);
        }
    });

    viewPager.setCurrentItem(getActionBar().getSelectedNavigationIndex(), true);
}

当返回到TabbedFragment时,所选的标签以及其后面一个标签将没有任何内容,只有一个空视图。但是如果我选择当前标签的下两个标签,内容就会被加载。然后返回到第一个标签时,内容将重新加载。
例如,我有A、B、C、D四个标签。在离开TabbedFragment之前,我选择了标签A。当返回到TabbedFragment时,我仍然停留在标签A上,但它是空的。标签B也是如此。
但是当选择标签C时,它会被创建并加载。返回到标签A时,它会被重新创建。

这里可能出了什么问题?


1
你在Fragment中有一个ViewPager,那么选项卡的用途是什么?你能提供实际的结构吗? - Triode
1
这就是全部了,你还需要什么? - Martynas Jurkus
1
好的,快速提问。你在一个活动中有一个带有ViewPager的碎片,并且在其中还有一个ViewPager吗? - kandroidj
1
是的,Activity 中有一个带有 ViewPager 的 Fragment,它显示了一些子 Fragment。 - Martynas Jurkus
1
你是如何解决它的? - Divya Motiwala
1
不得不扩展适配器并强制销毁片段以避免存储它们的状态。 - Martynas Jurkus
6个回答

90

5
这在支持v4的FragmentManager中如何工作? - wsgeorge
如何在从另一个片段返回时将ViewPager设置为第一页? - Maulik Dodia

4

好的,使用FragmentStatePagerAdapter时,当你导航超过一个片段时,你的片段将被销毁,因为默认情况下offScreenPageLimit设置为1,就像上面提到的那样。

通常,这个类用于具有大量Fragments的活动,即必须滚动浏览大量视图。如果您的应用程序不需要超过3-4个选项卡,我建议改用FragmentPagerAdapter,然后将您的offScreenPageLimit指定为3之类的东西,这样如果您到达第四个选项卡,前面的所有3个选项卡仍将在内存中。

这里是一些示例代码,可以在我的github项目上查看,说明如何动态加载片段,如果您不想添加offScreenPageLimit

https://github.com/lt-tibs1984/InterfaceDemo/blob/master/src/com/divshark/interfacedemo/InterfaceDemoMain.java

浏览此类中的所有代码,您将看到我如何动态加载碎片,每次滑动我的ViewPager时,尤其是在底部。

您可以下载此代码,并将其用作您想要执行的测试基础。 尝试在viewPager的onCreate()方法中添加setOffScreenPageLimit(2),并注意不同的行为。 要检查行为,请编辑片段1中的文本。导航离开并返回,是否设置了该选项。 您会发现,当设置它时,片段的文本仍然保持您更改的内容,因为片段永远不会重新创建。

如果您有其他问题,请提供。

祝你好运

更新

private static final String [] fragmentClasses = {"com.example.project.YourFragment1","com.example.project.YourFragment2","com.example.project.YourFragment3"};



 viewPager.setAdapter(new FragmentStatePagerAdapter(getFragmentManager()) {

    @Override
    public Fragment getItem(int position) {

       Fragment fragmentAtPosition = null;

       // $$$$ This is the Important Part $$$$$
       // Check to make sure that your array is not null, size is greater than 0 , current position is greater than equal to 0, and position is less than length
       if((fragmentClasses != null) && (fragmentClasses.length > 0)&&(position >= 0)&& (position < fragmentClasses.length))
        {
       // Instantiate the Fragment at the current position of the Adapter
        fragmentAtPosition = Fragment.instantiate(getBaseContext(), fragmentClasses[position]);
        fragmentAtPosition.setRetainInstance(true);

       }

        return fragmentAtPosition;
    }

    @Override
    public int getCount() {
        return fragmentClasses.length;
    }
});

好的,你的示例代码和我的一样。唯一的区别是适配器。如果我在某些情况下使用FragmentPagerAdapter,有时甚至不会在第一次加载片段内容。 - Martynas Jurkus

2
问题存在于您用作选项卡的片段中。它们似乎在恢复时不显示任何内容(请参见片段生命周期)。只有当前选定的+/-1个选项卡为空的“奇怪”问题是因为您的ViewPageroffScreenPageLimit默认为1。超过此阈值的所有选项卡都将被重新创建。
因此,在您的情况下增加该值将导致所有选项卡在恢复后都为空。请检查您的片段代码,了解您使用哪些生命周期方法来填充布局、设置适配器等,因为这就是造成您麻烦的原因。

是的,增加 offScreenPageLimit 后所有选项卡都变为空白了。我进行了一些尝试,看起来当恢复时它们根本没有被添加到 ViewPager 中。我只能看到空的 ViewPager - 片段具有不同的背景,以便更容易进行调试。 - Martynas Jurkus
而且它不依赖于Fragment,当恢复这些片段时,没有调用任何片段生命周期方法。尝试了几种不同的实现... - Martynas Jurkus
我认为增加值并不能解决问题,因为offScreenPageLimit会加载ViewPager的正负页数。 - techieWings
是的,我的意思不是增加值就能“解决”问题——它只是解释为什么只有当前的+/-1个选项卡为空。 - saschoar

1
我猜这是因为在加载片段时,Android会同时加载当前片段和下一个片段。如果你进行调试,你不会看到立即下一个片段的onPause被调用。
你可以在TabHost.OnTabChangeListener的onTabChanged()方法中以编程方式重新加载内容。

我没有使用TabHost,而是使用ActionBar选项卡。 - Martynas Jurkus
好的。那么你尝试在 onTabSelected() 方法中重新加载它了吗? - techieWings
我也发现问题是两个片段正在加载,但当前片段为空。即使现在还没有找到解决方案。谢谢。 - MindRoasterMir

1
经过多次研究,以下方法对我有效。我有一个复杂的布局,在一个片段中有3个选项卡,而这些选项卡将被切换成其他片段。我意识到,即使按下主屏幕按钮,ViewpagerAdapter仍将保留状态。我的问题是来回切换会使子片段UI视图元素为空并崩溃。关键在于不要新建ViewPagerAdapter。为适配器添加空值检查对我有用。此外,确保根据需要分配setOffscreenPageLimit()。另外,据我所知,对于具有UI的片段,不应使用setRetainInstance(true);,它专为无头片段设计。

在持有选项卡的片段中:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_tab, container, false);
    tabLayout = (TabLayout) view.findViewById(R.id.tablayout);
    viewPager = (ViewPager) view.findViewById(R.id.viewPager);

    //Important!!! Do not fire the existing adapter!!
    if (viewPagerAdapter == null) {
        viewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager());
        viewPagerAdapter.addFragments(new AFragment(), "A");
        viewPagerAdapter.addFragments(new BFragment(), "B");
        viewPagerAdapter.addFragments(new CFragment(), "C");
    }
    //Allocate retention buffers for three tabs, mandatory
    viewPager.setOffscreenPageLimit(3);
    tabLayout.setupWithViewPager(viewPager);
    viewPager.setAdapter(viewPagerAdapter);

    return view;
}

这个安全吗?我的意思是,它不会导致内存泄漏吧? - Juan Ignacio Barisich

0

或者更简单的方法是,当返回到tabbedfragment时(假设您使用了一个意图,并且该片段在一个活动中),请使用:

intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

这将保留原始活动并将其移动到堆栈顶部,而不是重新创建它,因此您永远不需要重新创建viewPager。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接