Android ViewPager的FragmentPagerAdapter中Fragment不重新載入資料,並且使用的是其他快取的Framgnet的問題
阿新 • • 發佈:2019-01-09
問題描述:
場景:幾個相同的頁面,要左右切換,比如:天氣3的首頁
ViewPager使用FragmentPagerAdapter,新新增進adapter中的資料沒有問題,而刪除資料減少資料之後,Fragment的資料內容沒有對號入座,沒有變化,而內容顯示的是其他已經刪除的Fragment的資料。
已經急不可耐的小夥伴可以翻到文末檢視解決方法
為什麼會導致這個原因呢,是因為之前的Fragmet是快取在記憶體中的,翻看FragmentPagerAdapter原始碼可以看到
@Override public Object instantiateItem(ViewGroup container, int如果Fragment不存在,那麼會呼叫getItem方法重新取得Fragment,並且新增進事務管理中,也就是快取在記憶體中,等待Activity被銷燬之後才釋放,反之已經存在的Fragment將不在重新建立,所以有時getItem方法有時候沒有回撥的原因就是因為,已經取了快取內的資料了。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; }
諸位看到這裡是不是感覺找到真正原因了,其實不然!
毫於疑問這段程式碼完全沒有問題,那問題出在哪兒呢?
相信諸位已經注意到了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; }
如果有說得不對的地方請諸位指出,共同學習!