安卓開發之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,並沒有觸發資源及重建的過程。

到此這篇關於安卓開發之FragmentPagerAdapter和FragmentStatePagerAdapter詳解的文章就介紹到這瞭,更多相關FragmentPagerAdapter和FragmentStatePagerAdapter詳解內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: