Android ViewPager.OnPageChangeListener生命周期轮换问题

时间:2014-10-14 04:07:53

标签: android android-viewpager fragment android-lifecycle

当屏幕旋转时,Android会在调用FragmentPagerAdapter.instantiateItem之前调用OnPageChangeListener.onPageSelected事件。

这似乎从根本上打破了我。我通常在调用instantiateItem期间获得对片段的引用。由于首先调用onPageSelected,因此我对片段的引用为null。解决此问题的最佳方法是什么?请参阅下面的代码以获得更好的解释:

(代码是使用Mono for Android用C#编写的,但应该与Java等价的几乎相同)。

适配器:

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;
    }

    ...
}

监听器:

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
    }

    ...
}

2 个答案:

答案 0 :(得分:1)

虽然这个确切的问题让我感觉很糟糕,但我认为我已经找到了“预期的使用模式”,其中它实际上没有被打破,而是按预期工作。 :/

首先,当用户旋转设备并重建视图层次结构时,永远不会将调用instantiateItem ...而是实际上将相关片段保存并恢复为FragmentManager的一部分(我认为),以避免调用instantiateItem。因此,对OnPageSelected的调用没有“排序”问题。

作为具有您自己状态的PagerAdapter的子类(即Fragments),您负责自己保存/恢复对片段的引用(使用传递给构造函数的FragmentManager)。我相信以下内容应该适用于FragmentStatePagerAdapter FragmentPagerAdapter子类:

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);
            }

        }
    }
}

答案 1 :(得分:1)

仅供参考,我还提出了以下解决方法:

public class TabPagerListener : Java.Lang.Object, ViewPager.IOnPageChangeListener, TabPageIndicator.IOnTabReselectedListener
{
    public ViewPager ViewPager { get; set; }

    public void OnPageSelected(int tabIndex)
    {
        ViewPager.Post(() => //this causes action to be delayed until after fragment is resumed
        {           
            var tabActivity = (ITabActivity)Instance;
            var currentFragment = TabAdapter.Fragments[TabPager.CurrentItem];
            //currentFragment is not null, InstantiateItem  was already called
        }
    }

    ...
}