最近遇到比拟奇怪的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 公布!