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);
[本章完...]
网友评论