美文网首页Android应用开发那些事四大组件
ViewPager<第三篇>:与Fragment结合使用

ViewPager<第三篇>:与Fragment结合使用

作者: NoBugException | 来源:发表于2019-12-20 14:29 被阅读0次

    ViewPager往往和Fragment一起使用,这样不仅可以将Fragment强制添加切换效果,还可以在填充ViewPager时将View替换为Fragment,使其具有生命周期

    Fragment是Activity的碎片,所以Fragment和Activity一样具有生命周期,所以ViewPager和Fragment结合使用的框架已在很多大型项目上使用。

    【适配器如何选择】

    ViewPager必须使用PagerAdapter适配器,当结合Fragment时,官方给我们提供了两种适配器:FragmentPagerAdapterFragmentStatePagerAdapter,这两种适配器都是PagerAdapter的子类。

    那么,应该如何选择呢?

    • 当页面比较多时,一般选择FragmentStatePagerAdapter

    解析:当直接使用PagerAdapter时,必须重写destroyItem方法,FragmentStatePagerAdapter已经为我们写好destroyItem的实现,源码如下:

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment) object;
    
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object
                + " v=" + ((Fragment)object).getView());
        while (mSavedState.size() <= position) {
            mSavedState.add(null);
        }
        mSavedState.set(position, fragment.isAdded()
                ? mFragmentManager.saveFragmentInstanceState(fragment) : null);
        mFragments.set(position, null);
    
        mCurTransaction.remove(fragment);
        if (fragment == mCurrentPrimaryItem) {
            mCurrentPrimaryItem = null;
        }
    }
    

    其中核心代码是:

    mCurTransaction.remove(fragment);
    

    FragmentStatePagerAdapter会销毁不需要的fragment。事务提交后, activity的FragmentManager中的fragment会被彻底移除。 FragmentStatePagerAdapter类名中的“state”表明:在销毁fragment时,可在onSaveInstanceState(Bundle)方法中保存fragment的Bundle信息。用户切换回来时,保存的实例状态可用来恢复生成新的fragment。

    • 当页面比较少、简单时,一般选择FragmentPagerAdapter

    解析:当直接使用PagerAdapter时,必须重写destroyItem方法,FragmentPagerAdapter已经为我们写好destroyItem的实现,源码如下:

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        Fragment fragment = (Fragment) object;
    
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        if (DEBUG) Log.v(TAG, "Detaching item #" + getItemId(position) + ": f=" + object
                + " v=" + (fragment.getView()));
        mCurTransaction.detach(fragment);
        if (fragment == mCurrentPrimaryItem) {
            mCurrentPrimaryItem = null;
        }
    }
    

    其核心代码是:

        mCurTransaction.detach(fragment);
    

    对于不再需要的fragment,FragmentPagerAdapter会选择调用事务的detach()方法来处理它,而非remove()方法。也就是说, FragmentPagerAdapter只是销毁了fragment的视图, fragment实例还保留在FragmentManager中。因此,FragmentPagerAdapter创建的fragment永远不会被销毁。

    • 当页面需要刷新时,选择FragmentStatePagerAdapter

    解析:

    如果使用FragmentPagerAdapter,Fragment对象一般不会被销毁的,这时需要修改ViewPager适配器中的传值,再调用以下方法刷新数据

    myPagerAdapter2.notifyDataSetChanged();
    

    但是,如果ViewPager页面比较多时,内存中会有大量的Fragment对象无法释放,浪费了内存。另外,由于ViewPager的自身特性,在内存中默认存在3个页面,其余页面会自动销毁,这样导致的情况是:ViewPager页面销毁了,但是对应的Fragment对象没有被销毁,我认为这是一个严重的内存泄漏现象。

    如果使用FragmentStatePagerAdapter的话,当ViewPager页面被销毁时,Fragment对象也会被销毁,就不存在这样的内存泄漏问题。

    【基本代码实现】

    MyPagerAdapter2.java

    public class MyPagerAdapter2 extends FragmentStatePagerAdapter {
    
        private Context mContext;
        private List<MyPagerFragment> myPagerFragmentList;
    
        public MyPagerAdapter2(Context context , List<MyPagerFragment> list, @NonNull FragmentManager fm, int behavior) {
            super(fm, behavior);
            mContext = context;
            myPagerFragmentList = list;
        }
    
    
        @Override
        public int getCount() {
            return myPagerFragmentList.size();
        }
    
        @NonNull
        @Override
        public Fragment getItem(int position) {
            return myPagerFragmentList.get(position);
        }
    
        @Override
        public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
            super.destroyItem(container, position, object);
        }
    }
    

    MyPagerFragment.java

    public class MyPagerFragment extends Fragment {
    
        String mContent;
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            mContent = (String) getArguments().get("content");
            View view = inflater.inflate(R.layout.item_base, container, false) ;
            TextView textView = (TextView) view.findViewById(R.id.tv);
            textView.setText(mContent);
            return view;
        }
    
    }
    

    item_base.xml

    <?xml version="1.0" encoding="utf-8"?>
    <TextView
        android:id="@+id/tv"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/ll_shape"
        android:textColor="#000000"
        android:gravity="center"
        android:textStyle="bold"
        android:textSize="200sp">
    </TextView>
    

    调用代码:

        viewpager = findViewById(R.id.viewpager);
    
        final List<MyPagerFragment> list = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            MyPagerFragment fragment = new MyPagerFragment();
            Bundle bundle = new Bundle();
            bundle.putString("content",String.valueOf(i));
            fragment.setArguments(bundle);
    
            list.add(fragment);
        }
        MyPagerAdapter2 myPagerAdapter2 = new MyPagerAdapter2(this,list, getSupportFragmentManager(), FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
        viewpager.setAdapter(myPagerAdapter2);
    

    [本章完...]

    相关文章

      网友评论

        本文标题:ViewPager<第三篇>:与Fragment结合使用

        本文链接:https://www.haomeiwen.com/subject/pqqgkctx.html