I'm having some problems when dealing with Fragment using ViewPager.
What I'm having:
An activity (say, MainActivity
) that contains a ViewPager
to display some Fragment(s). Some of them contains a callback interface, which will be called to do somethings in the MainActivity
.
The MainActivity
has a FragmentPagerAdapter
class, which is used as the adapter of the ViewPager
. And a List<Fragment>
in FragmentPagerAdapter
to store some Fragment that will be displayed on the ViewPager
.
What I'm expecting:
First launch, the Fragment called the callback interface's methods when I hit a button in it and MainActivity did somethings inside that. It worked great.
After a screen rotation, I expected it to work the same as the first launch BUT
NullPointerException: attempt to invoke a method on a null reference object (particularly, the Fragment's interface) hit me in my face.
What I know:
- The getItem(int position)
won't be called again once the Fragment is created. Instead the instantiateItem(ViewGroup container, int position)
will be called.
- FragmentManager
will store some Fragment in mActive.mValues
- ViewPager and fragments — what's the right way to store fragment's state? (I did reference to this and some other same topics on StackOverflow too.)
What I have tried and saw:
- Override instantiateItem(ViewGroup container, int position)
- Debugged for 1 day. I saw that when I pass getSupportFragmentManager()
in MainActivity's onCreate()
method to FragmentPagerAdapter
's super constructor, in the first launch, it has an "address in memory", assume it was '1a1a1a1'. The mActive.mValues
of FragmentManager
saved some Fragment' "address in memory" which are identical to the List<Fragment>
containing them (assume it was 'qwertyu'). Which meaned it was right.
But when I rotated the screen, passing the getSupportFragmentManager()
again, the "address in memory" was completely different, assume '9f9f9f9'. And FragmentManager
's mActive.mValues
contained a different set of Fragment' "address in memory" too (assume 'abcdeff'), although the number of Fragment in it was equal to the number of Fragment that was saved on the first launch (before rotation).
I have added a Fragment to the List<Fragment>
with a new "address in memory" (assume 'abababa'), has the callback interface. But when I hit the button in it, it was the Fragment that was in the FragmentManager
's mActive.mValues
after the rotation (with "address in memory" is 'abcdeff' as I assumed above), and that one didn't have the callback interface (due to not being set in MainActivity
first). And caused the NullPointerException as mentioned above.
My questions now is:
- First of all, how to get rid of this problem!? It would be better to keep using FragmentPagerAdapter
instead of another class. But I will consider using other class too.
- Second, can you explain why FragmentManager
saved the Fragment instance before rotation. But after rotation, it creates a completely different Fragment instance but still uses it instead of the Fragment that was saved in the List<Fragment>
?
Here is the code (I think I didn't use the instantiateItem(ViewGroup container, int position)
method in the right way so it still caused the problem).
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();
}
}
}
Please help. I'm being insane for 2 days now...!