1. 程式人生 > >Android ViewPager的FragmentPagerAdapter中Fragment不重新載入資料,並且使用的是其他快取的Framgnet的問題

Android ViewPager的FragmentPagerAdapter中Fragment不重新載入資料,並且使用的是其他快取的Framgnet的問題

問題描述:

場景:幾個相同的頁面,要左右切換,比如:天氣3的首頁

ViewPager使用FragmentPagerAdapter,新新增進adapter中的資料沒有問題,而刪除資料減少資料之後,Fragment的資料內容沒有對號入座,沒有變化,而內容顯示的是其他已經刪除的Fragment的資料。

已經急不可耐的小夥伴可以翻到文末檢視解決方法

為什麼會導致這個原因呢,是因為之前的Fragmet是快取在記憶體中的,翻看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; }
如果Fragment不存在,那麼會呼叫getItem方法重新取得Fragment,並且新增進事務管理中,也就是快取在記憶體中,等待Activity被銷燬之後才釋放,反之已經存在的Fragment將不在重新建立,所以有時getItem方法有時候沒有回撥的原因就是因為,已經取了快取內的資料了。

諸位看到這裡是不是感覺找到真正原因了,其實不然!

毫於疑問這段程式碼完全沒有問題,那問題出在哪兒呢?

相信諸位已經注意到了makeFragmentName這個方法,每個新新增的Fragment都添加了一個Tag。

  final long itemId = getItemId(position);
// Do we already have this fragment?
String name = makeFragmentName(container.getId(), itemId);
Fragment fragment = mFragmentManager.findFragmentByTag(name);

正是通過這個Tag才能讓做到讓每個Fragment是唯一的。問題就出在Tag上!

相信有許多小夥伴都習慣這樣寫,把getItemId的返回值直接返回position:

@Override
public long getItemId(int position) {
    return position;
}
那麼問題來了,position有沒有可能重複呢,答案當然是肯定的。正是新增的Fragment以position的名義打上了Tag,下一次資料減少的時候,又通過相同的position拿到了相同的Fragment,這裡就也就重複了。

有點亂,舉個粟子:


看到這裡答案已經明顯了,因為傳的ID不對,所以導致取小花的時候把小明遺留在記憶體的Fragment取出來了,而導致了Fragment取錯的原因。

程式碼很簡單:

1、重寫getItemId方法(推薦)

@Override
public long getItemId(int position) {
    return 這裡換成自己的唯一ID;
}

2、暴力重寫instantiateItem與destroyItem(不推薦)

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    container.removeView(((Fragment) object).getView()); // 移出viewpager兩邊之外的page佈局
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Fragment fragment = getItem(position);
    if (!fragment.isAdded()) { // 如果fragment還沒有added
FragmentTransaction ft = mFragmentManager.beginTransaction();
ft.add(fragment, fragment.getClass().getSimpleName());
ft.commit();
mFragmentManager.executePendingTransactions();//同步的方式新增Fragment
}

    if (fragment.getView().getParent() == null) {
        container.addView(fragment.getView()); // 為viewpager增加布局
}
    return fragment;
}

如果有說得不對的地方請諸位指出,共同學習!