Issue
When the screen is rotated Android calls the OnPageChangeListener.onPageSelected event prior to calling FragmentPagerAdapter.instantiateItem.
This seems fundamentally broken to me. I typically get a reference to the fragment during the call to instantiateItem. Since onPageSelected is called first my reference to the fragment is null. What's the best way to work around this problem? See code below for a better explanation:
(Code is written in C# using Mono for Android but should be nearly identical to the Java equivalent).
Adapter:
public class MainPagerAdapter : FragmentPagerAdapter
{
public Fragment[] Fragments { get; private set; }
public override Java.Lang.Object InstantiateItem(ViewGroup view, int index)
{
var o = base.InstantiateItem(view, index);
Fragments[index] = (Fragment)o;
return o;
}
...
}
Listener:
public class TabPagerListener : Java.Lang.Object, ViewPager.IOnPageChangeListener, TabPageIndicator.IOnTabReselectedListener
{
public void OnPageSelected(int tabIndex)
{
var tabActivity = (ITabActivity)Instance;
var currentFragment = TabAdapter.Fragments[TabPager.CurrentItem];
//currentFragment will be null since InstantiateItem hasn't been called yet
}
...
}
Solution
While it definitely feels broken when this exact issue bit me, I think I've figured out the "intended usage model", in which it's not actually broken but working as intended. :/
First, when the user rotates the device and the view hierarchy is reconstructed, it never will call instantiateItem...instead it actually saves and restores the relevant fragments as part of the FragmentManager (I think), to avoid calling instantiateItem. So there is no "ordering" problem with the call to OnPageSelected.
As a subclasser of PagerAdapter with your own state (namely, Fragments), you are responsible for saving/restoring your reference to the fragments yourself (using the FragmentManager that gets passed in to the constructor). I believe the following should work for both FragmentStatePagerAdapter and FragmentPagerAdapter subclasses:
public EventInfoPagerAdapter(FragmentManager fm) {
super(fm);
mFragmentManager = fm;
}
private static String STATE_SUPERCLASS = "STATE_SUPERCLASS";
private static String STATE_FRAGMENT_IDS = "STATE_FRAGMENT_IDS";
@Override
public Parcelable saveState() {
Parcelable p = super.saveState();
Bundle bundle = new Bundle();
bundle.putParcelable(STATE_SUPERCLASS, p);
Bundle fragmentsBundle = new Bundle();
for (int i=0; i<mFragments.size(); i++) {
Fragment f = mFragments.get(i);
if (f != null) {
mFragmentManager.putFragment(fragmentsBundle, Integer.toString(i), f);
}
}
bundle.putBundle(STATE_FRAGMENT_IDS, fragmentsBundle);
return bundle;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
Bundle bundle = (Bundle)state;
if (bundle != null) {
super.restoreState(bundle.getParcelable(STATE_SUPERCLASS), loader);
Bundle fragmentsBundle = bundle.getBundle(STATE_FRAGMENT_IDS);
mFragments.clear();
mFragments.ensureCapacity(fragmentsBundle.size());
Iterable<String> keys = fragmentsBundle.keySet();
for (String key: keys) {
int index = Integer.parseInt(key);
Fragment f = mFragmentManager.getFragment(fragmentsBundle, key);
if (f != null) {
while (mFragments.size() <= index) {
mFragments.add(null);
}
FragmentCompat.setMenuVisibility(f, false);
mFragments.set(index, (EventInfoFragment)f);
} else {
Log.w(LOG_TAG, "Bad fragment at key " + key);
}
}
}
}
Answered By - Mike Lambert
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.