問題記錄
背景
使用TabLayout+ViewPager+FragmentPagerAdapter實(shí)現(xiàn)經(jīng)典的多Tab底部展示的布局結(jié)構(gòu)。
需求
動(dòng)態(tài)增刪換Fragment
問題癥狀
刪除異常刁憋,增加失敗挠羔,替換失敗
問題原因
問題主要還是在于FragmentPagerAdapter的緩存機(jī)制:
FragmentPagerAdapter
會緩存所有的經(jīng)過instantiate()
之后的Fragment
眼五,保存在一個(gè)ArrayList<Fragment>
的數(shù)據(jù)結(jié)構(gòu)中案腺。在從緩存中取出來復(fù)用的時(shí)候日戈,是根據(jù)Container的ID+ItemId共同判斷的另凌。這個(gè)containerId
是哪個(gè)Container
呢谱轨?就是ViewPager
。所以吠谢,決定緩存取出策略的土童,基本就是itemId
了。
由此也可以復(fù)現(xiàn)問題場景:
假設(shè)有4個(gè)
Fragment
工坊,現(xiàn)在要將下表為1的那個(gè)Fragment
刪除献汗。你直接從你的List<Fragment>
中刪除了它,然后當(dāng)你點(diǎn)擊第1個(gè)時(shí)王污,顯示的卻還是原來那個(gè)罢吃。
類似的,增加/替換昭齐,也是同樣的癥狀尿招。
解決辦法
很簡單,就是給每個(gè)
Fragment
都固定一個(gè)ID阱驾,在FragmentPagerAdapter
的getItemId()
方法中根據(jù)各種不同的展現(xiàn)情景返回就谜,而不是默認(rèn)的只返回position
。
在這里里覆,最簡單的方式就是返回你的List<Fragment>
中Fragment
的hashCode
丧荐。
這樣就解決了增刪替換的問題,如果要調(diào)換位置租谈,也是可以的。
相關(guān)系統(tǒng)源碼
READ THE FUCKING SOURCE CODE
FragmentPagerAdapter
:
@Override
public Object instantiateItem(ViewGroup container, int position) {
......
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) {
......
mCurTransaction.attach(fragment);
} else {
fragment = getItem(position);
......
mCurTransaction.add(container.getId(), fragment,
makeFragmentName(container.getId(), itemId));
}
......
return fragment;
}
public long getItemId(int position) {
return position;
}
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
FragmentManager
:
public abstract Fragment findFragmentByTag(String tag);
@Override
public Fragment findFragmentByTag(String tag) {
if (mAdded != null && tag != null) {
// First look through added fragments.
for (int i=mAdded.size()-1; i>=0; i--) {
Fragment f = mAdded.get(i);
if (f != null && tag.equals(f.mTag)) {
return f;
}
}
}
......
return null;
}
其中mAdded
聲明是這樣的:
ArrayList<Fragment> mAdded;
好了,看了以上的源碼足夠解決問題了割去。