将页面添加到ViewPager

时间:2015-09-23 21:58:09

标签: android android-fragments android-viewpager android-adapter

尝试以编程方式将片段页面添加到我的ViewPager中,我得到:

java.lang.IllegalStateException: The application's PagerAdapter changed the adapter's contents without calling PagerAdapter#notifyDataSetChanged!
Expected adapter item count: 3, found: 2
Pager id: com.my.app:id/view_pager
Pager class: class android.support.v4.view.ViewPager
Problematic adapter: class com.my.app.ui.BaseFragmentPagerAdapter
            at android.support.v4.view.ViewPager.populate(ViewPager.java:1000)
            at android.support.v4.view.ViewPager.populate(ViewPager.java:952)
            at ...

我只是在FragmentPagerAdapter实施中调用这几行:

adapter.addFragment(new Fragment(), "FIRST");
adapter.addFragment(new Fragment(), "SECOND");
pager.setAdapter(adapter);

//later... (on click of a button)
adapter.addFragment(new Fragment(), "THIRD");
adapter.notifyDataSetChanged();

它实际上添加了第三页,但是当我尝试在那里滑动时,它会因上述异常而失败。直到今天,我还以为我对适配器的工作方式有了非常全面的了解。现在我无法弄清楚出了什么问题。

从调试开始,似乎所有时间adapter.getCount()都正确地返回3(在添加第三页之后),但是当我到达第三页时它最终会返回2并且中断,就像有人调用{ {1}},但那不是我。

这是我的简单课程:

destroyItem()

请注意,如果我使用public class BaseFragmentPagerAdapter extends FragmentPagerAdapter { private SparseArray<Fragment> mFragments; private ArrayList<String> mFragmentTitles; public BaseFragmentPagerAdapter(FragmentManager manager) { super(manager); this.mFragments = new SparseArray<>(); this.mFragmentTitles = new ArrayList<>(); } public void addFragment(Fragment f, String title) { this.mFragments.append(mFragments.size() , f); this.mFragmentTitles.add(title); } @Override public Fragment getItem(int position) { return this.mFragments == null ? null : this.mFragments.get(position) ; } @Override public int getItemPosition(Object object) { return this.mFragments.indexOfValue((Fragment) object); } @Override public void destroyItem(ViewGroup container, int position, Object object) { super.destroyItem(container, position, object); this.mFragments.remove(position); this.mFragmentTitles.remove(position); } @Override public CharSequence getPageTitle(int position) { return mFragmentTitles.get(position); } @Override public int getCount() { return mFragmentTitles.size(); } } 而不是FragmentStatePagerAdapter,则不会发生任何变化。

1 个答案:

答案 0 :(得分:0)

我会自己回答这个问题,因为我在写这个问题的时候找到了答案(经常)。我不确定这是最好的解决方案,但它确实有效。

基本上,当转到第3页时,由于它不能直接转换到,因此适配器将在第1页上调用destroyItem()。不应该FragmentPagerAdapter保留所有项目记忆而不破坏它们?

好吧,我确实通过mFragments字段在内存中保存了片段。对destroyItem()的调用会破坏片段的相关视图,但不应该破坏片段本身(我可能在这里略有错误,但你明白了。)

因此,您(我)将片段保留在内存中,并且使destroyItem()上的片段无效。具体来说,我不得不删除这两行:

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        super.destroyItem(container, position, object);
        //removed: this.mFragments.remove(position);
        //removed: this.mFragmentTitles.remove(position);
    }

这种方式getCount()保持正确返回3,当您返回到第1页时,适配器可以通过getItem()获取其片段。

修改

在处理了一天后,我可以说,乍一看,在记忆中没有片段的FragmentPagerAdapter对我来说毫无意义。

据记载,它应该只用于一些静态片段。默认情况下,ViewPager包含当前项目,前一个项目和后一个项目,但您可以通过viewPager.setOffscreenPageLimit()调整此设置。

如果你在destroyItem()期间销毁它们,情况就会变糟。通过getItem()中的某些逻辑很容易重新实现它们,但是很难保存它们的实例状态。例如,onSaveInstanceState(Bundle outstate)之后不会调用destroyItem()

如果你的片段很多并且你想接受其中一个片段被破坏的可能性,你只需切换到FragmentStatePagerAdapter,但那是另一个故事:它会自动调用onSaveInstanceState和让你保留你需要保留的东西。

使用FragmentPagerAdapter,因此放弃FragmentStatePagerAdapter的状态保存功能,如果您不保留则毫无意义。我的意思是,要么保留实例,要么保存状态(如评论中所示)。但是对于后者,我会选择FragmentStatePagerAdapter,这样可以轻松实现。

注意:我不是在讨论活动被销毁时保留实例,而是在ViewPager的页面经过destroyItem并且关联的片段通过{{1}时} 的)。