美文网首页
ViewPager(二):与Fragment的结合

ViewPager(二):与Fragment的结合

作者: bug音音 | 来源:发表于2021-01-03 17:43 被阅读0次

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

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

【适配器如何选择】

ViewPager必须使用PagerAdapter适配器,当结合Fragment时,官方给我们提供了两种适配器:FragmentPagerAdapter和FragmentStatePagerAdapter,这两种适配器都是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/lavvoktx.html