向 ViewPager 添加页面
Adding a page to ViewPager
尝试以编程方式将片段页面添加到我的 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()
正确 returns 3(添加第三页后),但是当我到达第三页时它最终 returns 2 和中断,好像有人在上面调用 destroyItem()
,但那不是我。
这是我的简单 class:
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
而不是 FragmentPagerAdapter
,则没有任何变化。
我会自己回答这个问题,因为我在写问题时(经常)找到了答案。我不确定这是最好的解决方案,但它确实有效。
基本上,当转到第 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页时,adapter可以通过getItem()
.
获取它的fragment
编辑
处理了一天之后,我可以说,乍一看,FragmentPagerAdapter
在内存中不保存片段对我来说毫无意义。
据记载,它应该只用于一些静态片段。 ViewPager
,默认情况下,保留当前项目,前一项和后一项,但您可以通过 viewPager.setOffscreenPageLimit()
.
调整此设置
如果你在 destroyItem()
期间摧毁它们,事情就会变得糟糕。通过getItem()
中的一些逻辑重新实例化它们会很容易,但是保存它们的实例状态是相当困难的。例如,onSaveInstanceState(Bundle outstate)
不会在 destroyItem()
之后调用。
如果你的碎片很多并且你想接受一个更多的碎片被摧毁的可能性,你只需切换到 FragmentStatePagerAdapter
,但这是另一回事:它会自动调用 onSaveInstanceState
并让你保留你需要保留的。
使用FragmentPagerAdapter
,因此放弃FragmentStatePagerAdapter
的状态保存功能,如果不保留就没有意义。我的意思是,要么保留实例,要么保存它们的状态(如评论中所建议的那样)。不过,对于后者,我会选择 FragmentStatePagerAdapter
,这很容易。
(注意:我不是在谈论在 activity 被销毁时保留实例,而是在 ViewPager 的页面经过 destroyItem
和相关片段时经过 onDestroyView()
).
尝试以编程方式将片段页面添加到我的 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()
正确 returns 3(添加第三页后),但是当我到达第三页时它最终 returns 2 和中断,好像有人在上面调用 destroyItem()
,但那不是我。
这是我的简单 class:
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
而不是 FragmentPagerAdapter
,则没有任何变化。
我会自己回答这个问题,因为我在写问题时(经常)找到了答案。我不确定这是最好的解决方案,但它确实有效。
基本上,当转到第 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页时,adapter可以通过getItem()
.
编辑
处理了一天之后,我可以说,乍一看,FragmentPagerAdapter
在内存中不保存片段对我来说毫无意义。
据记载,它应该只用于一些静态片段。 ViewPager
,默认情况下,保留当前项目,前一项和后一项,但您可以通过 viewPager.setOffscreenPageLimit()
.
如果你在 destroyItem()
期间摧毁它们,事情就会变得糟糕。通过getItem()
中的一些逻辑重新实例化它们会很容易,但是保存它们的实例状态是相当困难的。例如,onSaveInstanceState(Bundle outstate)
不会在 destroyItem()
之后调用。
如果你的碎片很多并且你想接受一个更多的碎片被摧毁的可能性,你只需切换到 FragmentStatePagerAdapter
,但这是另一回事:它会自动调用 onSaveInstanceState
并让你保留你需要保留的。
使用FragmentPagerAdapter
,因此放弃FragmentStatePagerAdapter
的状态保存功能,如果不保留就没有意义。我的意思是,要么保留实例,要么保存它们的状态(如评论中所建议的那样)。不过,对于后者,我会选择 FragmentStatePagerAdapter
,这很容易。
(注意:我不是在谈论在 activity 被销毁时保留实例,而是在 ViewPager 的页面经过 destroyItem
和相关片段时经过 onDestroyView()
).