• 深入分析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; iFragment 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 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,并没有触发资源及重建的过程。

  • 相关阅读:
    神经网络(四)前馈神经网络
    Codeforces Round 905 (Div. 3)ABCDEF
    洛谷bfs题2---P1825 [USACO11OPEN] Corn Maze S
    【licode】srtp链接问题
    # Apifox前后端开发人员使用场景
    idea开发工具常用的插件合集汇总
    螺旋矩阵、旋转矩阵、矩阵Z字打印
    自动驾驶人工智能
    ArrayList和linkedList的区别精简概述
    Redis与分布式:集群搭建
  • 原文地址:https://www.cnblogs.com/yuanting/p/16590977.html