乐趣区

关于android:深入分析FragmentPagerAdapter和FragmentStatePagerAdapter

最近遇到比拟奇怪的 bug,TableLayout+ViewPager 实现点击顶部 tab 切换 viewpager 视图。然而在 Viewpager 设置 dapter 时,最开始设置的是 FragmentPagerAdapter,会导致 tab 切换后 FragmentPagerAdapter 内的视图未刷新(与上一个 tab 内容反复或展现成空白,展现成空白个别呈现在页面重启后不能实现刷新胜利)。替换成 FragmentStatePagerAdapter 或者 FragmentStateAdapter,便解决了这一问题。这其实是个比拟常见的 bug,网络上有很多举荐的解决方案。那么到底 FragmentPagerAdapter、FragmentStateAdapter 以及 FragmentStatePagerAdapter 有何具体的区别呢?在这篇文章中我将具体解答。

依据类图进行剖析

FragmentPagerAdapter 与 FragmentPagerStateAdapter 区别点:

一:二者在状态保留有差别:FragmentPagerAdapter 并未实现 saveState()、restoreState()
public class FragmentPagerAdapter{
    // ......
    public static final int POSITION_UNCHANGED = -1;
    public static final int POSITION_NONE = -2;
 
    public Parcelable saveState() {return null;}
 
    public void restoreState(Parcelable state, ClassLoader loader) {}}

而 FragmentPagerStateAdapter 则实现了 saveState()、restoreState() 这俩办法:

 public Parcelable saveState() {
        Bundle state = null;
        if (mSavedState.size() > 0) {state = new Bundle();
            Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()];
            mSavedState.toArray(fss);
            state.putParcelableArray("states", fss);
        }
        for (int i=0; i<mFragments.size(); i++) {Fragment f = mFragments.get(i);
            if (f != null && f.isAdded()) {if (state == null) {state = new Bundle();
                }
                String key = "f" + i;
                mFragmentManager.putFragment(state, key, f);
            }
        }
        return state;
    }
 
    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {if (state != null) {Bundle bundle = (Bundle)state;
            bundle.setClassLoader(loader);
            Parcelable[] fss = bundle.getParcelableArray("states");
            mSavedState.clear();
            mFragments.clear();
            if (fss != null) {for (int i=0; i<fss.length; i++) {mSavedState.add((Fragment.SavedState)fss[i]);
                }
            }
            Iterable<String> keys = bundle.keySet();
            for (String key: keys) {if (key.startsWith("f")) {int index = Integer.parseInt(key.substring(1));
                    Fragment f = mFragmentManager.getFragment(bundle, key);
                    if (f != null) {while (mFragments.size() <= index) {mFragments.add(null);
                        }
                        f.setMenuVisibility(false);
                        mFragments.set(index, f);
                    } else {Log.w(TAG, "Bad fragment at key" + key);
                    }
                }
            }
        }
    }

FragmentStatePagerAdapter 对 Fragment 的状态进行了保留

二:二者在视图治理办法差别:

FragmentStatePagerAdapter 是整个 Fragment 对象的移除和重建

 public Object instantiateItem(ViewGroup container, int position) {if (mFragments.size() > position) {Fragment f = mFragments.get(position);
            if (f != null) {return f;}
        }
 
        if (mCurTransaction == null) {mCurTransaction = mFragmentManager.beginTransaction();
        }
 
        // 实例化 fragment(交给咱们实现的 getItem 办法)
        Fragment fragment = getItem(position);
 
        if (mSavedState.size() > position) {Fragment.SavedState fss = mSavedState.get(position);
            if (fss != null) {fragment.setInitialSavedState(fss);
            }
        }
        // 如果缓存 <= ViewPager 传入的 position, 阐明以后地位还未存入缓存.
        while (mFragments.size() <= position) {
            // 先占个坑
            mFragments.add(null);
        }
        fragment.setUserVisibleHint(false);
        // 填坑
        mFragments.set(position, fragment);
        // 填充视图
        mCurTransaction.add(container.getId(), fragment);
        return fragment;
    }
 
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {Fragment fragment = (Fragment) object;
 
        if (mCurTransaction == null) {mCurTransaction = mFragmentManager.beginTransaction();
        }
        // 从缓存中移除
        mFragments.set(position, null);
        // 从 FragmentManager 中移除
        mCurTransaction.remove(fragment);
    }

FragmentPagerAdapter 是视图的 attach 和 detach, 不会对整个 fragment 进行齐全的增加和删除操作。

因而,可见二者在应用场景上不同,如果页面较少,仍旧心愿可能将生成的 Fragment 保留在内存中,在须要显示的时候间接调用。而不要产生生成、销毁对象的额定开销。这样效率最高。这种状况下,选中 FragmentPagerAdapter 更适合。
对于在应用 FragmentPagerAdapter 呈现白屏或者刷新不了的 bug, 除了替换成 FragmentStatePagerAdapter,还须要重载 getItem() 和 instantiateItem() 对象。

对于 getItemPosition() 办法,两个累的区别是:FragmentStatePagerAdapter 会在因 POSITION_NONE 触发调用的 destroyItem 中真正的开释资源,从新建设一个新的 Fragment;而 FragmentPagerAdapter 仅仅会在 destoryItem() 中 detach 这个 Fragment,在 instantiateItem() 时会应用旧的 Fragment,并触发 attach, 并没有触发资源及重建的过程。

本文由博客一文多发平台 OpenWrite 公布!

退出移动版