使用新片段动态更新 ViewPager
Dynamically updating ViewPager with new fragments
我的应用程序中有一个简单的寻呼机布局,它应该根据我的 AppState 进行更改。该应用程序有两个主要状态,LoggedIn 和 LoggedOut。如果 AppState 是 LoggedOut,那么我的选项卡布局应该基于两个片段 fragment_login
和 fragment_about
构建。如果 App State 是 LoggedIn 那么选项卡布局应该基于不同的选项卡构建 fragment_main
, fragment_details
, fragment_about
, fragment_logout
.
我可以使用这些状态中的任何一种来初始化应用程序,并且一切都按预期工作。但是,问题是在用户登录后在运行时动态更改这些布局。
选项卡布局正确更改标题,但一些旧的选项卡片段仍然存在。
设置
我的 Class 连接片段及其各自的标题
private class FragmentList {
Class fClass;
String fName;
public FragmentList(Class c, String n) {
fClass = c;
fName = n;
}
}
我的部分适配器
你可以看到我目前正在尝试将 ArrayList 作为参数传递以便能够 reloadtabs()
,但这仍然不起作用
public class SectionsPagerAdapter extends FragmentPagerAdapter {
ArrayList<FragmentList> tabList;
public SectionsPagerAdapter(FragmentManager fm, ArrayList<FragmentList> fragList) {
super(fm);
tabList = fragList;
}
public void reloadTabs() {
tabList = listFragments_CurrentlyActive;
this.notifyDataSetChanged();
}
@Override
public Fragment getItem(int position) {
Fragment fragment = null;
Class newFragmentClass;
if (position <= tabList.size()) {
newFragmentClass = tabList.get(position).fClass;
} else {
newFragmentClass = tabList.get(0).fClass;
}
try {
fragment = (Fragment) newFragmentClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//Bundle args = new Bundle();
//args.putInt(ARG_SECTION_NUMBER, sectionNumber);
//fragment.setArguments(args);
return fragment;
}
@Override
public int getCount() {
return tabList.size();
}
@Override
public CharSequence getPageTitle(int position) {
if (position <= tabList.size()) {
return tabList.get(position).fName;
} else {
return "Unknown";
}
}
}
初始化片段列表
注意:每个片段都有自己的 public FRAGMENTNAME
静态字符串
private ArrayList<FragmentList> listFragmentsLoggedOut = new ArrayList<>();
private ArrayList<FragmentList> listFragmentsLoggedIn = new ArrayList<>();
listFragmentsLoggedOut.add(new FragmentList(fragment_login.class, fragment_login.FRAGMENT_NAME));
listFragmentsLoggedOut.add(new FragmentList(fragment_about.class, fragment_about.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_main.class, fragment_main.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_details.class, fragment_details.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_about.class, fragment_about.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_logout.class, fragment_logout.FRAGMENT_NAME));
构建 TabLayouts
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initApp();
refreshTabsBasedOnNewState();
}
//Separate method that can be called from elsewhere to update the Tabs
public void refreshTabsBasedOnNewState() {
//Can swap these and the app displays and functions correctly.
listFragments_CurrentlyActive = listFragments_LoggedOut;
//listFragments_CurrentlyActive = listFragments_LoggedIn;
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager(), listFragments_CurrentlyActive);
//update adapter class
mSectionsPagerAdapter.reloadTabs();
sectionsAdapterACTIVE.notifyDataSetChanged();
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(sectionsAdapterACTIVE);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
}
当前输出
当应用程序被硬编码为 LoggedIn
时,两个片段和选项卡会正确显示。与被硬编码为 LoggedOut
的应用程序相同。但是,当我尝试更改布局时,通过更改 listFragments_CurrentlyActive
的值然后我得到以下行为:
- 标签从 2 个增加到 4 个并且有正确的标题(和顺序)
- 关联的碎片是 2 个原始碎片和 2 个新碎片。
实际上,选项卡是正确的,但显示的片段是:
fragment_login
, fragment_about
, fragment_about
, fragment_logout
.
因此您可以看到正确添加了 2 个附加片段,但两个初始片段仍然引用 LoggedOut
个片段。
可能有一个非常简单的解决方案,我需要在某处使用 mSectionsPagerAdapter.notifyDataSetChanged()
更新适配器或重建它,但我在任何地方都找不到修复程序。非常感谢。 J
编辑
我什至尝试在开始时创建适配器,因此它们是完全独立的,但仍然会出现相同的行为
SectionsPagerAdapter sectionsAdapter_LoggedOut = new SectionsPagerAdapter(getSupportFragmentManager(), listFragmentsLoggedOut);
SectionsPagerAdapter sectionsAdapter_LoggedIn = new SectionsPagerAdapter(getSupportFragmentManager(), listFragmentsLoggedIn);
SectionsPagerAdapter sectionsAdapterACTIVE;
sectionsAdapterACTIVE = (AppState.isLoggedIn) ? sectionsAdapter_LoggedIn : sectionsAdapter_LoggedOut
getSupportFragmentManager()
中的片段缓存是否有问题?
编辑 2
添加此图片以帮助可视化问题。不确定我事先是否足够清楚。
已解决
问题在另一个线程Answer中描述如下
If you want to switch out the actual fragments that are being
displayed, you need to avoid FragmentPagerAdapter
and use
FragmentStatePagerAdapter
.
FragmentPagerAdapter
不会工作,因为它在第一次显示后永远不会破坏片段,因此我遇到了缓存问题。但是,我没有像建议的答案那样覆盖 getItemPosition(Object item)
。当我更换时,我的问题就消失了:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
与
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
我的应用程序中有一个简单的寻呼机布局,它应该根据我的 AppState 进行更改。该应用程序有两个主要状态,LoggedIn 和 LoggedOut。如果 AppState 是 LoggedOut,那么我的选项卡布局应该基于两个片段 fragment_login
和 fragment_about
构建。如果 App State 是 LoggedIn 那么选项卡布局应该基于不同的选项卡构建 fragment_main
, fragment_details
, fragment_about
, fragment_logout
.
我可以使用这些状态中的任何一种来初始化应用程序,并且一切都按预期工作。但是,问题是在用户登录后在运行时动态更改这些布局。
选项卡布局正确更改标题,但一些旧的选项卡片段仍然存在。
设置
我的 Class 连接片段及其各自的标题
private class FragmentList {
Class fClass;
String fName;
public FragmentList(Class c, String n) {
fClass = c;
fName = n;
}
}
我的部分适配器
你可以看到我目前正在尝试将 ArrayList 作为参数传递以便能够 reloadtabs()
,但这仍然不起作用
public class SectionsPagerAdapter extends FragmentPagerAdapter {
ArrayList<FragmentList> tabList;
public SectionsPagerAdapter(FragmentManager fm, ArrayList<FragmentList> fragList) {
super(fm);
tabList = fragList;
}
public void reloadTabs() {
tabList = listFragments_CurrentlyActive;
this.notifyDataSetChanged();
}
@Override
public Fragment getItem(int position) {
Fragment fragment = null;
Class newFragmentClass;
if (position <= tabList.size()) {
newFragmentClass = tabList.get(position).fClass;
} else {
newFragmentClass = tabList.get(0).fClass;
}
try {
fragment = (Fragment) newFragmentClass.newInstance();
} catch (Exception e) {
e.printStackTrace();
}
//Bundle args = new Bundle();
//args.putInt(ARG_SECTION_NUMBER, sectionNumber);
//fragment.setArguments(args);
return fragment;
}
@Override
public int getCount() {
return tabList.size();
}
@Override
public CharSequence getPageTitle(int position) {
if (position <= tabList.size()) {
return tabList.get(position).fName;
} else {
return "Unknown";
}
}
}
初始化片段列表
注意:每个片段都有自己的 public FRAGMENTNAME
静态字符串private ArrayList<FragmentList> listFragmentsLoggedOut = new ArrayList<>();
private ArrayList<FragmentList> listFragmentsLoggedIn = new ArrayList<>();
listFragmentsLoggedOut.add(new FragmentList(fragment_login.class, fragment_login.FRAGMENT_NAME));
listFragmentsLoggedOut.add(new FragmentList(fragment_about.class, fragment_about.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_main.class, fragment_main.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_details.class, fragment_details.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_about.class, fragment_about.FRAGMENT_NAME));
listFragmentsLoggedIn.add(new FragmentList(fragment_logout.class, fragment_logout.FRAGMENT_NAME));
构建 TabLayouts
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initApp();
refreshTabsBasedOnNewState();
}
//Separate method that can be called from elsewhere to update the Tabs
public void refreshTabsBasedOnNewState() {
//Can swap these and the app displays and functions correctly.
listFragments_CurrentlyActive = listFragments_LoggedOut;
//listFragments_CurrentlyActive = listFragments_LoggedIn;
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager(), listFragments_CurrentlyActive);
//update adapter class
mSectionsPagerAdapter.reloadTabs();
sectionsAdapterACTIVE.notifyDataSetChanged();
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(sectionsAdapterACTIVE);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs);
tabLayout.setupWithViewPager(mViewPager);
}
当前输出
当应用程序被硬编码为 LoggedIn
时,两个片段和选项卡会正确显示。与被硬编码为 LoggedOut
的应用程序相同。但是,当我尝试更改布局时,通过更改 listFragments_CurrentlyActive
的值然后我得到以下行为:
- 标签从 2 个增加到 4 个并且有正确的标题(和顺序)
- 关联的碎片是 2 个原始碎片和 2 个新碎片。
实际上,选项卡是正确的,但显示的片段是:
fragment_login
, fragment_about
, fragment_about
, fragment_logout
.
因此您可以看到正确添加了 2 个附加片段,但两个初始片段仍然引用 LoggedOut
个片段。
可能有一个非常简单的解决方案,我需要在某处使用 mSectionsPagerAdapter.notifyDataSetChanged()
更新适配器或重建它,但我在任何地方都找不到修复程序。非常感谢。 J
编辑
我什至尝试在开始时创建适配器,因此它们是完全独立的,但仍然会出现相同的行为
SectionsPagerAdapter sectionsAdapter_LoggedOut = new SectionsPagerAdapter(getSupportFragmentManager(), listFragmentsLoggedOut);
SectionsPagerAdapter sectionsAdapter_LoggedIn = new SectionsPagerAdapter(getSupportFragmentManager(), listFragmentsLoggedIn);
SectionsPagerAdapter sectionsAdapterACTIVE;
sectionsAdapterACTIVE = (AppState.isLoggedIn) ? sectionsAdapter_LoggedIn : sectionsAdapter_LoggedOut
getSupportFragmentManager()
中的片段缓存是否有问题?
编辑 2
添加此图片以帮助可视化问题。不确定我事先是否足够清楚。
已解决
问题在另一个线程Answer中描述如下
If you want to switch out the actual fragments that are being displayed, you need to avoid
FragmentPagerAdapter
and useFragmentStatePagerAdapter
.
FragmentPagerAdapter
不会工作,因为它在第一次显示后永远不会破坏片段,因此我遇到了缓存问题。但是,我没有像建议的答案那样覆盖 getItemPosition(Object item)
。当我更换时,我的问题就消失了:
public class SectionsPagerAdapter extends FragmentPagerAdapter {
与
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {