是使用ViewPager+Fragment构建界面的时候,遇到了这样的需求:在ViewPager的第一个item中有一个Button,点击Button会将第一个item中的Fragment换成另外一个Fragment。之前想的是将adapter中List<Fragment>中的数据换掉,然后notifyDataSetChanged,发现无效。研究FragmentPagerAdapter的源码发现,
@Override
public Object instantiateItem(ViewGroup container, int position) {
if (mCurTransaction == null) {
mCurTransaction = mFragmentManager.beginTransaction();
}
final long itemId = getItemId(position);
// 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 {
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);
}
return fragment;
}
其在instantiateItem方法中并不是直接去List里面拿到Fragment的,而是先从FragmentManager中通过Tag找对应的Fragment,如果可以找到就不会去List里面拿了,这样无论再怎么更新list都是没用的,解决思路如下:
1、在重写instantiateItem方法,手动从FragmentManager将需要更新的Fragment移除,代码如下:
@Override
public Object instantiateItem(ViewGroup container, int position) {
// TODO Auto-generated method stub
if (position == 0)
removeFragment(container,position);
return super.instantiateItem(container, position);
}
private void removeFragment(ViewGroup container,int index) {
String tag = getFragmentTag(container.getId(), index);
Fragment fragment = fm.findFragmentByTag(tag);
if (fragment == null)
return;
FragmentTransaction ft = fm.beginTransaction();
ft.remove(fragment);
ft.commit();
ft = null;
fm.executePendingTransactions();
}
由于我这里要动态更换的是第一个item的fragment,所以就直接做判断if (position == 0),其他情况可以按需修改。首先通过一个getFragmentTag方法得到当前position的fragment的tag,这个方法里面其实就是用反射的方式调用了FragmentPagerAdapter的makeFragmentName方法,因为这里的tag是在这个方法中计算出来的,此段代码如下:
private String getFragmentTag(int viewId, int index) {
try {
Class<FragmentPagerAdapter> cls = FragmentPagerAdapter.class;
Class<?>[] parameterTypes = { int.class, long.class };
Method method = cls.getDeclaredMethod("makeFragmentName",
parameterTypes);
method.setAccessible(true);
String tag = (String) method.invoke(this, viewId, index);
return tag;
} catch (Exception e) {
e.printStackTrace();
return "";
}
}
得到Tag后就可以通过FragmentManager的findFragmentByTag方法得到fragment,如果存在,就通过FragmentTransaction 移除掉即可。
最后重写getItemPosition() 方法,返回 POSITION_NONE。代码如下:
@Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return POSITION_NONE;
}
2、使用FragmentStatePagerAdapter,它不会对fragment做缓存,每次都会销毁重建,这样效率很低,只适合页面非常少访问不频繁等特殊情况。