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

商匡云商
Logo
注册新帐户
对比商品
  • 合计 (0)
对比
0
购物车