使用 ViewPager 进行屏幕旋转 - FragmentManager 和 FragmentPagerAdapter



我在使用 ViewPager 处理片段时遇到了一些问题。

我有什么:
一个活动(例如,MainActivity),其中包含用于显示某些片段的ViewPager。其中一些包含一个回调接口,将被调用以在MainActivity中执行某些操作。
MainActivity有一个FragmentPagerAdapter类,用作ViewPager的适配器。还有一个List<Fragment>FragmentPagerAdapter来存储一些将在ViewPager上显示的片段。

我期待什么:
第一次启动时,当我点击回调接口中的按钮时,Fragment 调用了回调接口的方法,MainActivity 在其中做了一些事情。效果很好。
屏幕旋转后,我希望它的工作方式与第一次启动相同,
NullPointerException:尝试在空引用对象(特别是片段的接口)上调用方法,打了我的脸。

我所知道的:
- 创建片段后,不会再次调用getItem(int position)。相反,将调用instantiateItem(ViewGroup container, int position)
-FragmentManager将在mActive.mValues
中存储一些片段 - ViewPager 和片段 — 存储片段状态的正确方法是什么?(我也在StackOverflow上参考了这个和其他一些相同的主题。

我尝试过并看到的:
- 覆盖instantiateItem(ViewGroup container, int position)
- 调试了 1 天。我看到当我将 MainActivity 的onCreate()方法中的getSupportFragmentManager()传递给FragmentPagerAdapter的超级构造函数时,在第一次启动时,它有一个"内存中的地址",假设它是"1a1a1a1"。FragmentManagermActive.mValues保存了一些片段的"内存地址",这些地址与包含它们的List<Fragment>相同(假设它是"qwertyu">)。这意味着它是对的。
但是当我旋转屏幕,再次传递getSupportFragmentManager()时,"内存中的地址"完全不同,假设"9f9f9f9"。FragmentManagermActive.mValues也包含一组不同的片段"内存地址"(假设"abcdeff">),尽管其中的片段数量等于第一次发射时(旋转前)保存的片段数量。
我在List<Fragment>中添加了一个片段,其中包含一个新的"内存中的地址"(假设"abababa"),具有回调接口。但是当我点击其中的按钮时,它是旋转后FragmentManagermActive.mValues中的片段("内存中的地址"是我上面假设的"abcdeff">),并且那个没有回调接口(由于没有首先设置MainActivity)。并导致如上所述的NullPointerException

我现在的问题是:
- 首先,如何摆脱这个问题!?最好继续使用FragmentPagerAdapter而不是另一个类。但我也会考虑使用其他类。
- 其次,您能否解释一下为什么FragmentManager在旋转之前保存了片段实例。但是在旋转之后,它会创建一个完全不同的片段实例,但仍使用它而不是保存在List<Fragment>中的片段?

这是代码(我认为我没有以正确的方式使用instantiateItem(ViewGroup container, int position)方法,所以它仍然导致了问题)。

public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Attach the SectionsPagerAdapter to the ViewPager
SectionsPagerAdapter pagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.viewPager);
viewPager.setAdapter(pagerAdapter);
}

//
//
//Adapter class
private class SectionsPagerAdapter extends FragmentPagerAdapter {
private static final int PAGE_HOME = 0;
private int tabCount = 1;
private List<Fragment> fragmentList = new ArrayList<>();
private List<String> fragmentTitleList = new ArrayList<>();
//private FragmentManager fragmentManager;
SectionsPagerAdapter(FragmentManager fm) {
super(fm);
//fragmentManager = fm;
//Default HomeFragment
HomeFragment homeFragment = new HomeFragment();
//Callback interface
homeFragment.setOnCategoryFragmentChangedListener(new HomeFragment.OnCategoryFragmentChangedListener() {
//This method will be called when a button in HomeFragment is clicked
@Override
public void onAddNewCategory(String categoryName) {
addNewCategory(categoryName);
}
});
fragmentList.add(homeFragment);
fragmentTitleList.add("Home");
}
@Override
public Fragment getItem(int position) {
return fragmentList.get(position);
}
@Override
public int getCount() {
return tabCount;
}
@Override
public CharSequence getPageTitle(int position) {
return fragmentTitleList.get(position);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
fragmentList.set(position, (Fragment) super.instantiateItem(container, position));
return fragmentList.get(position);
}
private void addNewCategory(String categoryName) {
CategoryFragment fragment = new CategoryFragment();
tabCount += 1;
fragmentList.add(fragment);
fragmentTitleList.add(categoryName);
notifyDataSetChanged();
}
}
}

请帮忙。我已经疯了两天了...!

我相信android在方向更改期间重新启动活动,从而制作了FragmentPagerAdapter的多个实例和List的多个实例集。

我不完全理解你的问题,但我怀疑实例化项目无论如何都没有做任何事情。如果不覆盖实例化项()Fragment getItem(int pos)不起作用吗?

哦,好吧,在我感到睡眠之后,我找到了解决方案。确实,我没有以正确的方式使用instantiateItem()方法。再次调试后,我发现每当我滑动(或选择是否也使用 TabLayout)到另一个片段时,instantiateItem()方法都会被调用,甚至在getItem(int pos)之前收到调用,无论是第一次启动还是旋转后。这就是为什么我认为我们应该在instantiateItem()方法中为片段设置东西。
因此,这是我现在如何使用instantiateItem()方法:

@Override
public Object instantiateItem(ViewGroup container, int position) {
fragmentList.set(position, (Fragment) super.instantiateItem(container, position));
Fragment fragment = fragmentList.get(position);
if (position == PAGE_HOME) {
((HomeFragment) fragment).setOnCategoryFragmentChangedListener(new HomeFragment.OnCategoryFragmentChangedListener() {
@Override
public void onAddNewCategory(String categoryName) {
addNewCategory(categoryName);
}
});
}
return fragment;
}

如果有人有更好的解决方案,如果您不介意,请告诉我。我会考虑的。

最新更新