FragmentPagerAdapter和FragmentStatePagerAdapter是咱们开发中常常遇到的两个类,尤为是和ViewPager的配合。几乎咱们每一个Android开发者都被Fragment和ViewPager,PopupWindow,适配等等一堆神坑折磨着,尤为是Fragment神坑无数,这些都是每天在用的组件,Google为何留给咱们这么多坑。也正因如此,为了避免掉进坑里,就须要咱们不断去填坑。
下面是经过阅读FragmentPagerAdapter和FragmentStatePagerAdapter可以学到的知识点:java
咱们在使用ViewPager的时候,常用下面几种方式:android
ViewPager viewPager = findViewById(R.id.viewPager); // 方式一 viewPager.setAdapter(new PagerAdapter() { private String mTitles[] ; private List<View> mViewList; @Override public int getCount() { return mViewList.size(); } @Override public boolean isViewFromObject(View view, Object object) { return view == object; } @Override public Object instantiateItem(ViewGroup container, int position) { View view = mViewList.get(position); container.addView(view); return view; } @Override public void destroyItem(ViewGroup container, int position, Object object) { container.removeView((View) object); } @Override public CharSequence getPageTitle(int position) { return mTitles[position]; } }); // 方式二 viewPager.setAdapter(new FragmentPagerAdapter(getSupportFragmentManager()) { @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount() { return fragments.size(); } }); // 方式三 viewPager.setAdapter(new FragmentStatePagerAdapter(getSupportFragmentManager()) { @Override public Fragment getItem(int position) { return fragments.get(position); } @Override public int getCount() { return fragments.size(); } });
用法你们都比较熟悉了,其中FragmentPagerAdapter 和 FragmentStatePagerAdapter有什么区别呢?设计模式
根据两个类的名称就能够知道FragmentStatePagerAdapter彷佛是保存状态的,咱们分别去这两个类找下它们的区别,发现它们都重写了父类PageAdapter的方法:缓存
public abstract class PagerAdapter { // 省略 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) { } }
分别查看它们的实现:ide
FragmentPagerAdapter的实现布局
@Override public Parcelable saveState() { return null; } @Override public void restoreState(Parcelable state, ClassLoader loader) { }
FragmentStatePagerAdapter的实现性能
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的状态进行了保存,而FragmentPagerAdapter则是空实现。this
虽然两个Adapter均有保存状态的代码,可是它们具体是在哪里被调用的呢?根据咱们学过的Activity和Fragment的保存状态的方式,咱们知道状态的恢复和保存通常在这些组件或者View里,的确,它们是在ViewPager中。设计
public class ViewPager extends ViewGroup { @Override public Parcelable onSaveInstanceState() { Parcelable superState = super.onSaveInstanceState(); SavedState ss = new SavedState(superState); ss.position = mCurItem; if (mAdapter != null) { ss.adapterState = mAdapter.saveState(); } return ss; } @Override public void onRestoreInstanceState(Parcelable state) { if (!(state instanceof SavedState)) { super.onRestoreInstanceState(state); return; } SavedState ss = (SavedState) state; super.onRestoreInstanceState(ss.getSuperState()); if (mAdapter != null) { mAdapter.restoreState(ss.adapterState, ss.loader); setCurrentItemInternal(ss.position, false, true); } else { mRestoredCurItem = ss.position; mRestoredAdapterState = ss.adapterState; mRestoredClassLoader = ss.loader; } } }
由于ViewPager持有Adapter实例,因此ViewPager的onSaveInstanceState和onRestoreInstanceState方法都是间接调用Adapter来执行状态的恢复和保存的,咱们看到ViewPager中间接调用了mAdapter.saveState()
和mAdapter.restoreState
。代理
除了上面的区别外,FragmentStatePagerAdapter和FragmentPagerAdapter惟一的区别就是对Fragment对象的处理了。
咱们日常使用ViewPager + PageAdater时候须要重写不少方法,如开头的那几个案例,而ViewPager + FragmentPagerAdapter(FragmentStatePagerAdapter) 仅仅实现getItem和getCount两个方法就够了,核心方法instantiateItem和destroyItem内部已经作好了实现。
先看FragmentStatePagerAdapter类
private ArrayList<Fragment> mFragments = new ArrayList<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的两个实现方法:
@Override public Object instantiateItem(ViewGroup container, int position) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } final long itemId = getItemId(position); // Do we already have this fragment? String name = makeFragmentName(container.getId(), itemId); Fragment fragment = mFragmentManager.findFragmentByTag(name); if (fragment != null) { mCurTransaction.attach(fragment); } else { fragment = getItem(position); mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId)); } if (fragment != mCurrentPrimaryItem) { fragment.setMenuVisibility(false); fragment.setUserVisibleHint(false); } return fragment; } @Override public void destroyItem(ViewGroup container, int position, Object object) { if (mCurTransaction == null) { mCurTransaction = mFragmentManager.beginTransaction(); } mCurTransaction.detach((Fragment)object); }
FragmentStatePagerAdapter 内部还作了个小缓存,这个不是重点,咱们主要关注
FragmentStatePagerAdapter
mCurTransaction.add(container.getId(), fragment); mCurTransaction.remove(fragment);
和FragmentPagerAdapter
mCurTransaction.attach(fragment); mCurTransaction.add(container.getId(), fragment,makeFragmentName(container.getId(), itemId)); mCurTransaction.detach((Fragment)object);
很明显,FragmentStatePagerAdapter 对fragment进行彻底的添加和删除操做,而FragmentPagerAdapter 则是对视图进行attach和detach。
总结:
FragmentStatePagerAdapter 适合大量页面,不断重建和销毁
FragmentPagerAdapter 适合少许页面,常驻内存。
由于Android这个知识点有两个设计模式的案例实在太经典了,因此想顺便拿来说一下,理解了这些,开发过程当中常见两个坑的问题:
通过查看源码就很是容易解决了!
public class ViewPager { private PagerAdapter mAdapter; public void setAdapter(PagerAdapter adapter) { adapter.xx(); adapter.xxx(); this.mAdapte = adapter; // .... requestLayout(); } public void dataSetChanged() { final int adapterCount = mAdapter.getCount(); // .... mAdapter.destroyItem(this, ii.position, ii.object); // .... // .... } }
能够看到ViewPager持有的是PagerAdapter,ViewPager中间接调用了不少PagerAdapter的方法,使用组合方式来代替继承方式解耦。
怎么看着那么像模板方法模式呢,设计模式中不少模式确实太像了,好比代理模式 和 装饰器模式。
组合优于继承,总之,能用组合实现的不要用继承。
前不久我看一个开源项目的代码,大量的继承和模板方法模式,看的我真的快怀疑本身智商了。
再来看下ViewPager的代码:
public class ViewPager { // 观察者 private PagerObserver mObserver; private PagerAdapter mAdapter; public void setAdapter(PagerAdapter adapter) { adapter.xx(); adapter.xxx(); this.mAdapte = adapter; if (mAdapter != null) { if (mObserver == null) { // 实例化观察者对象 mObserver = new PagerObserver(); } // 传递一个观察者mObserver对象供adapter调用 mAdapter.setViewPagerObserver(mObserver); } // .... requestLayout(); } public void dataSetChanged() { final int adapterCount = mAdapter.getCount(); // .... mAdapter.destroyItem(this, ii.position, ii.object); // .... // .... } /** * 观察者对象 */ private class PagerObserver extends DataSetObserver { PagerObserver() { } @Override public void onChanged() { dataSetChanged(); } @Override public void onInvalidated() { dataSetChanged(); } } }
在setAdapter中 mAdapter.setViewPagerObserver(mObserver);
这里传递了一个内部类对象名称叫 PagerObserver,你们要注意了,这个地方虽然起名叫作观察者,我认为是不合理的,确实Android提供给咱们一个注册观察者的接口来监听(后面详细讲),不过咱们经常用notifyDataChange() 来通知ViewPager数据更新这里的默认实现 并无真正用 观察者模式,多是Google偷懒了吧。
mAdapter.setViewPagerObserver(mObserver)传递的这个对象 更像回调。回调接口的本质不就是传递一个对象吗?? C语言的实现则是传递指针。JavaScript传递function。
看下咱们常常调用的notifyDataSetChanged方法:
public abstract class PagerAdapter { // 被观察者,暂时不用管 private final DataSetObservable mObservable = new DataSetObservable(); // 冒充者,虽然也叫观察者对象,但实际算是个回调对象 private DataSetObserver mViewPagerObserver; void setViewPagerObserver(DataSetObserver observer) { synchronized (this) { mViewPagerObserver = observer; } } public void notifyDataSetChanged() { synchronized (this) { if (mViewPagerObserver != null) { mViewPagerObserver.onChanged(); } } mObservable.notifyChanged(); } }
注释写的很明白,PagerAdapter里面怎么可能同时充当观察者和被观察者嘛,notifyDataSetChanged没有用观察者模式实现。
可是咱们注意到了,notifyDataSetChanged方法的最后调用了 mObservable.notifyChanged();
这里才是真正的观察者模式,被观察者准备调用本身的方法通知全部的观察者 数据改变了。惋惜的是当前 目前还木有人注册,孤芳自赏!
这里暂时作个标记,咱们最后在看Android的观察者模式设计。
继续跟踪代码,notifyDataSetChanged调用了mViewPagerObserver
这个伪娘的onChanged方法(ViewPager中),
onChanged()调用了ViewPager的dataSetChanged方法:
void dataSetChanged() { // This method only gets called if our observer is attached, so mAdapter is non-null. final int adapterCount = mAdapter.getCount(); mExpectedAdapterCount = adapterCount; boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 && mItems.size() < adapterCount; // newCurrItem 用于跟踪标记当前ViewPager的所在页 int newCurrItem = mCurItem; boolean isUpdating = false; // 遍历ViewPager中全部的items(每一个ItemInfo中包含着fragment实例,position等信息) for (int i = 0; i < mItems.size(); i++) { final ItemInfo ii = mItems.get(i); // getItemPosition方法是咱们根据须要重写的方法,有三种值: POSITION_UNCHANGED和POSITION_NONE和pos(int类型) final int newPos = mAdapter.getItemPosition(ii.object); // (1). 若是getItemPosition()返回值是POSITION_UNCHANGED(默认实现),不作处理 if (newPos == PagerAdapter.POSITION_UNCHANGED) { continue; } // (2). 若是getItemPosition()返回值是POSITION_NONE,移除ViewPager的mItems中当前正在遍历着的ItemInfo if (newPos == PagerAdapter.POSITION_NONE) { mItems.remove(i); i--; if (!isUpdating) { // 方法内没什么实际意义 mAdapter.startUpdate(this); isUpdating = true; } // 同时调用adapter的销毁方法销毁当前遍历着的ItemInfo mAdapter.destroyItem(this, ii.position, ii.object); needPopulate = true; continue; } // (3). 若是getItemPosition()返回值是其余的值(如newPos = 3),则至关于把[首次初始化的ViewPager中ItemInfo的position]从新赋值为指定的值.换个位置,这个特性通常咱们不多用到. if (ii.position != newPos) { if (ii.position == mCurItem) { // 若是当前for循环中遍历的ItemInfo.position正好等于ViewPager中的当前页下标,跟踪标记 newCurrItem = newPos; } ii.position = newPos; needPopulate = true; } } if (isUpdating) { mAdapter.finishUpdate(this); } Collections.sort(mItems, COMPARATOR); // 根据前面的分析, (2) 和 (3)都会致使从新请求布局 if (needPopulate) { // Reset our known page widths; populate will recompute them. final int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); if (!lp.isDecor) { lp.widthFactor = 0.f; } } // 设置当前页,并从新布局或者是滚动到此页 setCurrentItemInternal(newCurrItem, false, true); requestLayout(); } }
这个方法逻辑稍有点多,分析都写在注释里了。
根据下面这段代码
boolean needPopulate = mItems.size() < mOffscreenPageLimit * 2 + 1 && mItems.size() < adapterCount;
咱们知道给ViewPager新添加View或Fragment没有任何问题.它会自动处理,可是更新 就有问题了,咱们可能但愿把某个页面替换掉,好比A->B.
可是根据这段代码的逻辑,不重写getItemPosition方法(默认POSITION_UNCHANGED)的话是不会有任何变化的。
经过重写getItemPosition()方法
final int newPos = mAdapter.getItemPosition(ii.object); // ... mAdapter.destroyItem(this, ii.position, ii.object);
咱们能够看到ViewPager中只要有返回POSITION_NONE的项,那么就会销毁该项并刷新。
可是不建议你们直接在adapter中这么干(虽然我是这么干的,懒人):
反例以下:
@Override public int getCount() { return this.mFragmentList.size(); } public int getItemPosition(Object object) { return POSITION_NONE; } @Override public Fragment getItem(int position) { return mFragmentList.get(position); }
这样会致使调用notifyDataChange时候ViewPager中每一个Fragment都会被 mAdapter.destroyItem。咱们只是想更新某个Item就够了,这一会儿所有都
destroyItem一遍,性能确定形成浪费。
你们能够根据本身的逻辑修改进行实现,其中object就是Fragment对象或view对象,好比设置tag之类的,只令某一项返回POSITION_NONE。
public int getItemPosition(Object object) { return POSITION_NONE; }
首先声明:虽然在ViewPager(充当观察者)和PagerAdapter(充当被观察者)中出现了观察者模式的代码,可是ViewPager中并未注册观察者。不过这里的案例很是经典,不禁得分析下做为记录。一样的,ListView(充当观察者)和BaseAdapter(充当被观察者)则使用了这个模式并在ListView中注册了观察者,有兴趣的能够查看相关源码。
前面讲了一个伪观察者模式,继续....
仍然是上次的代码,notifyDataSetChanged最后一行调用了
mObservable.notifyChanged() 这才是正宗的观察者模式。
public abstract class PagerAdapter { // 被观察者 private final DataSetObservable mObservable = new DataSetObservable(); public void notifyDataSetChanged() { synchronized (this) { // 冒充者 if (mViewPagerObserver != null) { mViewPagerObserver.onChanged(); } } // 正宗 mObservable.notifyChanged(); } public void registerDataSetObserver(DataSetObserver observer) { mObservable.registerObserver(observer); } public void unregisterDataSetObserver(DataSetObserver observer) { mObservable.unregisterObserver(observer); } }
如何在PagerAdapter 中给 被观察者DataSetObservable 注册一个观察者?
注意:这段代码仅作参考,Android并未真正注册。
PagerAdapter pagerAdapter = new .. ; pagerAdapter.registerDataSetObserver(new DataSetObserver() { @Override public void onChanged() { super.onChanged(); // .... 实现,这里copy伪娘那里的onChange()方法. } @Override public void onInvalidated() { super.onInvalidated(); // .... 实现 } });
下面咱们就来一块儿看看Android的被观察者和观察者是怎么写的,能够借鉴参考下:
被观察者
DataSetObservable
public class DataSetObservable extends Observable<DataSetObserver> { public void notifyChanged() { synchronized(mObservers) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onChanged(); } } } public void notifyInvalidated() { synchronized (mObservers) { for (int i = mObservers.size() - 1; i >= 0; i--) { mObservers.get(i).onInvalidated(); } } } }
Observable
package android.database; import java.util.ArrayList; public abstract class Observable<T> { protected final ArrayList<T> mObservers = new ArrayList<T>(); public void registerObserver(T observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized(mObservers) { if (mObservers.contains(observer)) { throw new IllegalStateException("Observer " + observer + " is already registered."); } mObservers.add(observer); } } public void unregisterObserver(T observer) { if (observer == null) { throw new IllegalArgumentException("The observer is null."); } synchronized(mObservers) { int index = mObservers.indexOf(observer); if (index == -1) { throw new IllegalStateException("Observer " + observer + " was not registered."); } mObservers.remove(index); } } public void unregisterAll() { synchronized(mObservers) { mObservers.clear(); } } }
观察者
package android.database; public abstract class DataSetObserver { public void onChanged() { // Do nothing } public void onInvalidated() { // Do nothing } }
这就是Android经典的观察者模式.
ListView和ViewPager在适配器模式和观察者模式存在诸多类似,触类旁通让咱们的理解更加透彻。
ListView在setAdapter() 中注册的观察者.
public void setAdapter(ListAdapter adapter) { if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); } resetList(); mRecycler.clear(); if (mHeaderViewInfos.size() > 0|| mFooterViewInfos.size() > 0) { mAdapter = wrapHeaderListAdapterInternal(mHeaderViewInfos, mFooterViewInfos, adapter); } else { mAdapter = adapter; } mOldSelectedPosition = INVALID_POSITION; mOldSelectedRowId = INVALID_ROW_ID; super.setAdapter(adapter); if (mAdapter != null) { // 注册观察者 mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); } requestLayout(); }