防止ViewPager销毁屏幕外视图

137

我有一个ViewPager,连接着一个FragmentPagerAdapter来展示三个片段。当它距离当前位置超过一个滑动时,ViewPager看起来会销毁托管片段的视图。

这些视图都是简单的列表,这种优化完全没有必要,所以我想禁用它。它会导致一些视觉问题,因为这些列表都有布局动画应用于它们,并且这些动画在它们被销毁和重建后重新播放。它还会每次显示滚动条简短的动画(其中滚动条短暂可见以指示可以滚动),这可能会分散注意力,并且用户的当前滚动位置也会在此过程中丢失。

ViewPager也不会加载第三个片段,直到第一次滑动发生,这是有问题的,因为每个片段都处理自己的服务调用,我更喜欢在活动加载时同时触发所有三个服务调用。延迟第三个服务调用不太理想。

是否有任何方法说服ViewPager停止这种行为并仅将所有片段保留在内存中?

4个回答

341
在支持包的第四次修订中,ViewPager添加了一种方法,允许您指定要使用的屏幕外页面数量,而不是默认值1。
在您的情况下,您想指定为2,这样当您在第三页时,第一页不会被销毁,反之亦然。
mViewPager = (ViewPager)findViewById(R.id.pager);
mViewPager.setOffscreenPageLimit(2);

4
你好。我正在工作的项目中,片段/页面是动态创建的,因此可能会有无限数量的片段。通常情况下最多只有10个或更少。如果使用这种解决方案来处理这么多页面,会不会浪费内存?顺便说一下,这些片段仅包含视图。谢谢! - mahkie
这个方法在我的应用中抛出了"java.lang.IllegalStateException: Fragment already added:"错误。 - alicanbatur
非常感谢。它真的起作用了。我也遇到了与@chefgon相同的问题。 - Prashant
谢谢,这个方法对我非常有帮助。 - Ranjitsingh Chandel
这让我抓狂了...谢谢! - darksider474
显示剩余2条评论

9

默认情况下,ViewPager在滑动页面时会重新创建片段。为了防止这种情况发生,您可以尝试以下两种方法之一:

1. 在片段的onCreate()中调用setRetainInstance(true)。

2. 如果片段数量固定且相对较小,则在onCreate()中添加以下代码:

ViewPager mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setOffscreenPageLimit(3);

如果我没记错,第二个选项更有前途。但是我建议你尝试两种方法,看看哪一种更有效。


3

2
所选的答案不错,但对我来说并不太好。这是因为我有很多片段(20-30),如果我使用了setOffscreenPageLimit(30),那么带有ViewPager的活动加载时间会很长。在我的情况下,我必须实现destroyItem()方法(在ViewPager的Adapter类中),并删除对super函数的调用。这是伪代码。
@Override                                                                                    
public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
                                                                                             
}                                                                                            

现在,如果您想要更多的上下文信息,请看整个ViewPagerAdapter类。
public class ViewPagerAdapter extends FragmentPagerAdapter {
    private final List<Fragment> listFragment =
            new ArrayList<>();
    public ViewPagerAdapter(@NonNull FragmentManager fm) {
        super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
    }

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

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

    /*This is the method I was taking about*/
    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
    
    }

    public void addFragment(Fragment fragment)
    {
        listFragment.add(fragment);
        notifyDataSetChanged();
    }
}

是的,这就是我在寻找的答案。谢谢! - Febin Mathew

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