在屏幕旋转时在 ViewPager 中显示包含数据的片段



所以在我的MainActivity OnCreate中,我创建了3个片段,每个片段都填充了不同的数据。然后,我将这些片段添加到我的 ViewPagerAdapter 中。

ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());
MealsFragment exploreFragment = null;
MealsFragment favoriteFragment = null;
MealsFragment localFragment = null;
//if there is no saved instance, create fragments with passed data as args and add them to the viewpageradapter
if(savedInstanceState == null){
exploreFragment = fetchExploreMealsDataAndCreateFragment();
favoriteFragment = fetchFavoriteMealsDataAndCreateFragment();
localFragment = fetchLocalMealsDataAndCreateFragment();
}
//add fragments to the adapter
viewPagerAdapter.addFragment(exploreFragment, "EXPLORE");
viewPagerAdapter.addFragment(favoriteFragment, "FAVORITES");
viewPagerAdapter.addFragment(localFragment, "LOCAL");
//set adapter to the viewpager and link it with the different tabs
mviewPager.setAdapter(viewPagerAdapter);
mtabLayout.setupWithViewPager(mviewPager);

3 种不同的获取方法从 API 或数据库获取数据,因此我不想每次旋转屏幕并完成生命周期时都调用这些方法。这就是为什么我首先检查保存的实例状态是否为空。但是现在发生的事情是,如果我的 savedInstanceState 不为空,则片段将为空,因为我以这种方式初始化它们。

显然这不是问题,因为当我旋转屏幕时,片段保持不变。我想知道幕后发生了什么,因为我认为这不是处理我的情况的正确方法。任何改进建议也值得赞赏。

提前感谢!

编辑:

忘了提到ViewPagerAdapter是我自己的FragmentPageAdapter实现

public class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> fragmentList = new ArrayList<>();
private final List<String> fragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int i) {
return fragmentList.get(i);
}
@Override
public int getCount() {
return fragmentList.size();
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
public void addFragment(Fragment fragment, String title){
fragmentList.add(fragment);
fragmentTitleList.add(title);
}

}

我想知道幕后

发生了什么

好的,所以,这将涉及查看FragmentPagerAdapter的来源。相关的是instantiateItem()方法的实施...虽然真的只是其中的一部分。

@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
[...]
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
[...]
}

实质上,ViewPagerAdapter中的getItem()方法在活动的生命周期内(即使在配置更改期间(只针对每个位置调用一次。除第一个对象外,每次直接从FragmentManager而不是适配器检索Fragment对象。

所以是的,对于您的活动的所有重新创建,您的适配器持有的List<Fragment>只是nullnullnull......但这并不重要,因为不会再次访问此列表。

然而

上述语句假定适配器中的每个片段都是在配置更改之前构造并添加到FragmentManager的,这不一定能保证。

默认情况下,ViewPager的屏幕外页面限制为1。这意味着您的第三页(您的localFragment(,不一定在首次启动时添加到ViewPager中,因此也不必添加到FragmentManager中。如果你滚动到下一页,哪怕是一次,它也会,但这不一定是真的。

或者,您可能已经手动将屏幕外页面限制设置为2,在这种情况下,将立即添加所有页面/片段。


也许最好的办法是完全改变你使用FragmentPagerAdapter的方式。我会把它作为一个内部类放在你的Activity里:

private class ExampleAdapter extends FragmentPagerAdapter {
public ExampleAdapter() {
super(getSupportFragmentManager());
}
@Override
public Fragment getItem(int position) {
switch (position) {
case 0: return fetchExploreMealsDataAndCreateFragment();
case 1: return fetchFavoriteMealsDataAndCreateFragment();
case 2: return fetchLocalMealsDataAndCreateFragment();
default: throw new IllegalArgumentException("unexpected position: " + position);
}
}
@Nullable
@Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0: return "EXPLORE";
case 1: return "FAVORITES";
case 2: return "LOCAL";
default: throw new IllegalArgumentException("unexpected position: " + position);
}
}
@Override
public int getCount() {
return 3;
}
}

然后你的onCreate()可以改成这样:

FragmentPagerAdapter viewPagerAdapter = new ExampleAdapter();
mviewPager.setAdapter(viewPagerAdapter);
mtabLayout.setupWithViewPager(mviewPager);

这样做的好处是,您只能按需调用"获取和创建"方法,但在配置更改之前未加载它们的情况下,仍然可以在配置更改后调用它们。

最新更新