转载请注明出处:http://blog.csdn.net/xiong199208/article/details/52729720
在上一篇博客中,介绍了两种ViewPager的懒加载,期间用到了ViewPager的FragmentPagerAdapter适配器,在开发过程中,经常会和FragmentStatePagerAdapter混淆使用,那么他们之间有什么区别呢?用一个demo进行测试:
首先测试FragmentPagerAdapter,在getItem方法和填充的Fragment的onCreateView和onActivityCreated方法打上log进行调用时机判断:
switch (position) {
case 0:
BaseFragment fragment01 = new Fragment01();
mFragments.add(fragment01);
System.out.println("Fragment01对应的getItem");
return fragment01;
case 1:
BaseFragment fragment02 = new Fragment02();
mFragments.add(fragment02);
System.out.println("Fragment02对应的getItem");
return fragment02;
case 2:
BaseFragment fragment03 = new Fragment03();
mFragments.add(fragment03);
System.out.println("Fragment03对应的getItem");
return fragment03;
case 3:
BaseFragment fragment04 = new Fragment04();
mFragments.add(fragment04);
System.out.println("Fragment04对应的getItem");
return fragment04;
public abstract class BaseFragment extends Fragment {
private ContentView mView;
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
System.out.println("onCreateView");
//防止不断创建新的视图对象
if (mView==null){
mView = new ContentView(getContext()) {
@Override
protected View getChrildView() {
return getFragmentView();
}
};
}
return mView;
}
/**
* 子类来负责填充自己的视图
* @return
*/
protected abstract View getFragmentView();
/**
* 主activity的onCreate()执行完后,该方法才执行
* @param savedInstanceState
*/
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//mView.initView();
System.out.println("onActivityCreated");
}
/**
* 加载视图
*/
public void loadData() {
mView.initView();
}
}
拖动ViewPager,显示Fragment02的内容,然后在拖回Fragment01,结果如下:
10-03 12:37:34.070 3530-3530/? I/System.out: Fragment01对应的getItem
10-03 12:37:34.070 3530-3530/? I/System.out: Fragment02对应的getItem
10-03 12:37:34.070 3530-3530/? I/System.out: onCreateView
10-03 12:37:34.070 3530-3530/? I/System.out: onActivityCreated
10-03 12:37:34.070 3530-3530/? I/System.out: onCreateView
10-03 12:37:34.070 3530-3530/? I/System.out: onActivityCreated
10-03 12:49:07.902 3530-3530/com.itheima.lazyviewpager I/System.out: Fragment03对应的getItem
10-03 12:49:07.902 3530-3530/com.itheima.lazyviewpager I/System.out: onCreateView
10-03 12:49:07.902 3530-3530/com.itheima.lazyviewpager I/System.out: onActivityCreated
10-03 12:49:18.898 3530-3530/com.itheima.lazyviewpager I/System.out: onCreateView
10-03 12:49:18.898 3530-3530/com.itheima.lazyviewpager I/System.out: onActivityCreated
因为预加载的原因,ViewPager加载了Fragment01,Fragment02,Fragment03,但在拖回Fragment01时,并没有调用ViewPager的getView方法,new一个新的Fragment01,而是直接复用了原来的Fragment01,再调用它的onCreateView和onActivityCreated方法。
我们来看一看FragmentPagerAdapter的源码:
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
mCurTransaction.attach(fragment);
} else {
//通过getView返回的Fragment
fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
if (fragment != mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
可以发现,在显示一个Fragment之前,有一个findFragmentByTag(name)操作,也就是说,当getView调用之前,FragmentManager都会去寻找是否已经加载过了,如果找到了,就不再调用getView方法。
总结:FragmentPagerAdapter会复用已经加载过的Fragment
FragmentStatePagerAdapter的测试过程不再赘述,直接来看源码:
// If we already have this item instantiated, there is nothing
// to do. This can happen when we are restoring the entire pager
// from its saved state, where the fragment manager has already
// taken care of restoring the fragments we previously had instantiated.
if (mFragments.size() > position) {
Fragment f = mFragments.get(position);
if (f != null) {
return f;
}
}
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
Fragment fragment = getItem(position);
if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment);
if (mSavedState.size() > position) {
Fragment.SavedState fss = mSavedState.get(position);
if (fss != null) {
fragment.setInitialSavedState(fss);
}
从源码可以了解到,已经加载过的Fragment仅仅保存了Fragment.SavedState对象。只是有助于我们恢复新的Fragment的各项数据,但并没有维护一个FragmentManager,来复用已经加载过的Fragment,这也就意味着,我们需要调用getView来创建新的Fragment对象。
结论:FragmentStatePagerAdapter不会复用Fragment
是否复用Fragment,需要在实际开发中进行具体判断,根据内存消耗问题,加载速度问题等进行选择。
问题来了,就是很倔强,一心就要用FragmentStatePagerAdapter来复用Fragment,怎么样才能实现呢?这时候就需要维护一个Fragment的工厂:
public class FragmentFactory {
//维护一个Fragment的集合
private Map<Integer,BaseFragment> mCaches = new HashMap<>();
//设计为单例模式
private static FragmentFactory sInstance;
private FragmentFactory(){}
public static FragmentFactory getInstance(){
if(sInstance == null){
sInstance = new FragmentFactory();
}
return sInstance;
}
public BaseFragment getFragment(int position){
//做一个集合记录下来所有已经加载过的fragment,下一次直接取出,这样保证了唯一性,模拟了FragmentManager的内存缓存
BaseFragment fragment;
if(mCaches.containsKey(position)){
//上一次已保存的情况,取出来即可
fragment = mCaches.get(position);
Log.e("fragment", "取出fragment");
return fragment;
}
switch (position){
case 0:
fragment = new Fragment01();
break;
case 1:
fragment = new Fragment02();
break;
case 2:
fragment = new Fragment03();
break;
case 3:
fragment = new Fragment04();
break;
default:
fragment = new Fragment01();
break;
}
mCaches.put(position, fragment);
Log.e("fragment", "保存fragment");
return fragment;
}
}
有了工厂类,具体的实现就很简单了:
/**
* 通过Fragment工厂来实现FragmentStatePagerAdapter对Fragment的复用
*/
class MyPagerAdapter extends FragmentStatePagerAdapter {
@Override
public CharSequence getPageTitle(int position) {
return title[position];
}
public MyPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
BaseFragment fragment = FragmentFactory.getInstance().getFragment(position);
return fragment;
}
@Override
public int getCount() {
return 4;
}
}