尝试以编程方式将片段页面添加到我的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
,则不会发生任何变化。
答案 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}时} 的)。