Android - 使用 FragmentStatePagerAdapter 保存/恢复状态
Posted
技术标签:
【中文标题】Android - 使用 FragmentStatePagerAdapter 保存/恢复状态【英文标题】:Android - Save/restore state with FragmentStatePageAdapter 【发布时间】:2016-10-05 17:26:41 【问题描述】:我做了这门课:
public class My_ViewPagerAdapter extends FragmentStatePagerAdapter
private ArrayList<My_Fragment> fragments=new ArrayList<>();
private ArrayList<String> tabTitles=new ArrayList<>();
public My_ViewPagerAdapter(FragmentManager fm)
super(fm);
@Override
public Fragment getItem(int position)
return fragments.get(position);
@Override
public int getItemPosition(Object object)
for (int i=0;i<fragments.size();i++)
Class<? extends My_Fragment> clazz= fragments.get(i).getClass();
if (clazz.isInstance(object))
return i;
return POSITION_NONE;
@Override
public int getCount()
return fragments.size();
public void addFragment(My_Fragment fragment, String tabTitle)
fragments.add(fragment);
tabTitles.add(tabTitle);
notifyDataSetChanged();
public void removeFragment(int position)
fragments.remove(position);
tabTitles.remove(position);
notifyDataSetChanged();
public My_Fragment getFragmentAtTab(int position)
return fragments.get(position);
@Override
public CharSequence getPageTitle(int position)
return tabTitles.get(position);
我正在动态添加/删除片段/选项卡,我想通过配置更改来保存它们的状态。
我实现了 onSaveInstanceState():
int tabs=tabLayout.getTabCount();
for (int i=0;i<tabs;i++)
outState.putInt("TabsCount",tabs);
outState.putString("Tab_"+i,getTabTitle(i));
outState.putString("FragmentClassAtTab_"+i,my_viewPagerAdapter.getFragmentAtTab(i).getClass().getName());
我实现了 onRestoreInstanceState():
for (int i=0;i<tabs;i++)
try
My_Fragment fragment= (My_Fragment) Class.forName(savedInstanceState.getString("FragmentClassAtTab_"+i)).newInstance();
addTab(fragment ,savedInstanceState.getString("Tab_"+i));
catch (ClassNotFoundException e)
e.printStackTrace();
catch (InstantiationException e)
e.printStackTrace();
catch (IllegalAccessException e)
e.printStackTrace();
addTab 方法是:
public void addTab(My_Fragment fragment, String title)
if (fragment!=null) my_viewPagerAdapter.addFragment(fragment,title);
tabLayout.addTab(tabLayout.newTab().setText(title));
问题:
当配置更改发生时,我添加的每个片段都会“自动”重新创建,因此每次都会调用其构造函数。 onCreateView 会在这些实例上调用,而不是在我的代码重新创建的实例上调用。
我需要 onCreateView 来调用在我的选项卡中实例化的片段,以便设置它们的列表视图。
“自动”创建的片段通过我的 onRestoreInstanceState 方法被新片段实例替换,但没有在这些新实例上调用 onCreateView 方法。
我认为我做错了,也许有更好的方法来通过配置更改保存选项卡和片段...感谢任何帮助。
【问题讨论】:
只是为了充分理解,FragmentStatePagerAdapter是由Activity还是Fragment持有的?配置更改后分片数是否正确?他们有预期的内容吗? 是的,适配器在我的活动的 onCreate() 方法中被保留和重新创建。如果说“片段数”是指片段.size(),是的,我曾经通过在我的适配器上调用“removeFragment”来删除片段,但是因为它在配置更改后重新创建(总是在 onCreate 方法中)我不'不需要手动删除它们。 认为我进一步的输入是有限的(离开 android 有一段时间了)。但是,您是否看过这篇 SO 帖子:***.com/questions/15313598/…?只是觉得它看起来很相关 感谢您的帮助,但是,我找到了一个棘手的解决方案(检查我自己的答案),它可以帮助我检索(重新)创建视图的片段.. 好吧,没问题,很高兴你成功了 :) 编码愉快 【参考方案1】:我曾经创造过类似的东西,在我的例子中是这样的:
public class DisplayPagerAdapter extends FragmentStatePagerAdapter
private static final String TAG = "DisplayPagerAdapter";
SparseArray<DisplayFragment> registeredFragments = new SparseArray<DisplayFragment>();
private final Context context;
private final DisplayCoreModule display;
private final FragmentManager fm;
private boolean isAddOrRemoving;
public DisplayPagerAdapter(Context context, FragmentManager fm,
DisplayCoreModule display)
super(fm);
this.context = context;
this.display = display;
this.fm = fm;
Log.d(TAG, "pages " + display.getPagesCount());
public void notifySizeChangingDataSetChange()
isAddOrRemoving = true;
notifyDataSetChanged();
isAddOrRemoving = false;
@Override
public int getCount()
int count = (display != null && display.getPagesCount() > 0) ? display
.getPagesCount() : 1;
return count;
@Override
public int getItemPosition(Object object)
DisplayFragment frag = (DisplayFragment) object;
if (!display.containsPageId(frag.getPageId()))
// this will update the 'no information' page with id -1
return POSITION_NONE;
if (isAddOrRemoving)
// recreate all for simplicity
return POSITION_NONE;
return POSITION_UNCHANGED;
@Override
public Fragment getItem(int position)
Log.d(TAG, "getItem " + position);
return DisplayFragment.newInstance(position);
@Override
public CharSequence getPageTitle(int position)
if (display != null && display.getPagesCount() > 0)
return context.getString(R.string.page) + " " + (position + 1);
else
return super.getPageTitle(position);
@Override
public Object instantiateItem(ViewGroup container, int position)
Log.d(TAG, "instantiateItem " + position);
DisplayFragment fragment = (DisplayFragment) super.instantiateItem(
container, position);
registeredFragments.put(position, fragment);
return fragment;
@Override
public void destroyItem(ViewGroup container, int position, Object object)
Log.d(TAG, "destroyItem " + position);
registeredFragments.remove(position);
super.destroyItem(container, position, object);
public Fragment getRegisteredFragment(int position)
return registeredFragments.get(position);
public SparseArray<DisplayFragment> getRegisteredFragments()
return registeredFragments;
很久没写了,没办法详细写。
但是,要注意的关键点(我认为)是我有一个外部“簿记员”(DisplayCoreModule)。每当 DisplayCoreModule 中的 Fragment 数量发生变化时,我只需调用 notifySizeChangingDataSetChange()
以使 FragmentStatePagerAdapter 刷新内容。
关于您的一些问题,FragmentStatePagerAdapter
不会调用您的片段的onCreateView
,直到它们显示出来。通常这意味着只有三个片段:您当前查看的一个片段和该片段任一侧的一个片段。
也许,想想看,你的问题的根源是缺少 getItemPosition()
和 POSITION_NONE。似乎还记得更新动态内容是必要的。使notifyDataSetChanged
有适当的效果。
【讨论】:
感谢您的回答!但我认为每当我添加/删除选项卡/片段时简单地调用“notifydataSetChanged()”方法与您的方法相似,所以问题仍然存在。 我明白你的意思。只是看起来有点像您更急切地管理适配器的片段,而我的 sn-p 或多或少只得到一个要求它创建实例的计数。我认为那里有区别。还在答案中添加了几行可能值得研究的内容。 我添加了 getItemPosition() 覆盖方法,但没有任何帮助...您建议做点别的吗?我真的不知道如何使这项工作。【参考方案2】:HACKY 解决方案:
我只是修改了我的 addTab 方法:
public void addTab(My_Fragment fragment, String title)
boolean createFragment=true;
if (getTabCount()>0)
//all tabs must have different names
for (int i=0;i<getTabCount();i++)
if (getTabTitle(i).equals(title)==true)
createFragment=false;
break;
if (createFragment && fragment!=null)
//this ensures that if a fragment of the same type already exists, that should be the one to be added, tabs are made of fragments of different classes, so there will be no problem while checking this.
for (Fragment f: my_activity.getSupportFragmentManager().getFragments())
if (fragment.getClass().isInstance(f))
my_viewPagerAdapter.addFragment((My_Fragment)f,title);
tabLayout.addTab(tabLayout.newTab().setText(title));
return;
my_viewPagerAdapter.addFragment(fragment,title);
tabLayout.addTab(tabLayout.newTab().setText(title));
【讨论】:
以上是关于Android - 使用 FragmentStatePagerAdapter 保存/恢复状态的主要内容,如果未能解决你的问题,请参考以下文章
想要使用cordova/android禁用android的HardBack按钮
Android 安装包优化Android 中使用 SVG 图片 ( 使用 appcompat 支持库兼容 5.0 以下版本的 Android 系统使用矢量图 )