FragmentPagerAdapter和FragmentStatePagerAdapter的区别

转载请注明出处: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;
        }
    }
github地址:https://github.com/xiong199208/FragmentPagerAdapter
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值