最近在项目中使用ViewPager+Fragment实现左右两个滑动切换界面。但是遇到一个问题:在不退出当前Activity的情况下,重新加载Fragment,却得到了空白界面,并没有生成新的Fragment添加进去。
这个问题可能是由于旧的Fragment没有被清除导致的。
查看了 FragmentPagerAdapter 类的创建实例的函数 instantiateItem :
@NonNull
public Object instantiateItem(@NonNull ViewGroup container, int position) {
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
long itemId = this.getItemId(position);
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
if (fragment != null) {
this.mCurTransaction.attach(fragment);
} else {
fragment = this.getItem(position);
this.mCurTransaction.add(container.getId(), fragment, makeFragmentName(container.getId(), itemId));
}
if (fragment != this.mCurrentPrimaryItem) {
fragment.setMenuVisibility(false);
fragment.setUserVisibleHint(false);
}
return fragment;
}
我们看到这个函数会先检查是否已经存在了这样一个Fragment,如果不存在才会去调用 getItem 方法产生一个新的Fragment。所以我们可以利用类似的方法先去FragmentManager中清除对应的Fragment,然后再让适配器重新加载新Fragment。如下我们重写 FragmentPagerAdapter 类:
public class ViewMaterialFragmentPagerAdapter extends FragmentPagerAdapter {
private FragmentManager mFragmentManager;
private FragmentTransaction mCurTransaction;
private Context mContext;
private List<Fragment> mFragmentArrayList;
private int[] pagerTitles = new int[]{R.string.text_material_qr_code, R.string.text_material_list};
public ViewMaterialFragmentPagerAdapter(FragmentManager fm, Context ctx, List<Fragment> fragments) {
super(fm);
mFragmentManager = fm;
mContext = ctx;
mFragmentArrayList = fragments;
}
@Override
public Fragment getItem(int i) {
return mFragmentArrayList.get(i);
}
@Override
public int getCount() {
return mFragmentArrayList.size();
}
@Override
public CharSequence getPageTitle(int position) {
return mContext.getString(pagerTitles[position]);
}
@NonNull
@Override
public Object instantiateItem(@NonNull ViewGroup container, int position) {
return super.instantiateItem(container, position);
}
/**
* 清除缓存fragment
* @param container ViewPager
*/
public void clear(ViewGroup container){
if (this.mCurTransaction == null) {
this.mCurTransaction = this.mFragmentManager.beginTransaction();
}
for(int i=0;i<mFragmentArrayList.size();i++){
long itemId = this.getItemId(i);
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = this.mFragmentManager.findFragmentByTag(name);
if(fragment != null){//根据对应的ID,找到fragment,删除
mCurTransaction.remove(fragment);
}
}
mCurTransaction.commitNowAllowingStateLoss();
}
/**
* 等同于FragmentPagerAdapter的makeFragmentName方法,
* 由于父类的该方法是私有的,所以在此重新定义
* @param viewId
* @param id
* @return
*/
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
}
代码重点是 clear 方法,只要在需要销毁已经加载过得Fragment的地方,调用这个clear方法,再让ViewPager重新设置适配器即可。
pagerAdapter.clear(mViewPager);
mViewPager.setAdapter(pagerAdapter);
clear 方法中的 makeFragmentName 方法,可以在 FragmentPagerAdapter 类源码中找到,此处的makeFragmentName与源码中一模一样,只是由于这个方法在源码中私有的,我就暂且复制过来使用吧,当然也可以使用反射调用。
############ 好了,调大音量,听首歌吧 ~ ###############