轉載請注明:(http://blog.csdn.net/u012854870/article/details/70231752)
先不廢話,直接上Adapter代碼:
public class SimpleFragmentPageAdapter extends FragmentPagerAdapter {
private List<BaseFragment> mFragments;
private FragmentManager fragmentManager;
private List<String> tags;
public SimpleFragmentPageAdapter(FragmentManager fm, List<BaseFragment> fragments) {
super(fm);
this.tags = new ArrayList<>();
this.fragmentManager = fm;
this.mFragments = fragments;
}
public void setNewFragments(List<BaseFragment> fragments) {
if (this.tags != null) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
for (int i = ; i < tags.size(); i++) {
fragmentTransaction.remove(fragmentManager.findFragmentByTag(tags.get(i)));
}
fragmentTransaction.commit();
fragmentManager.executePendingTransactions();
tags.clear();
}
this.mFragments = fragments;
notifyDataSetChanged();
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
tags.add(makeFragmentName(container.getId(), getItemId(position)));
Fragment fragment = (Fragment) super.instantiateItem(container, position);
this.fragmentManager.beginTransaction().show(fragment).commit();
return fragment;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
Fragment fragment = mFragments.get(position);
fragmentManager.beginTransaction().hide(fragment).commit();
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
接下來說一下為什麼要這樣寫
1.先看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;
}
private static String makeFragmentName(int viewId, long id) {
return "android:switcher:" + viewId + ":" + id;
}
從源碼中可以看到在從 FragmentManager 中取出 Fragment 時調用了 findFragmentByTag() 方法,而這個 Tag 是由 makeFragmentName() 方法生成的。繼續往下可以看到每一個 Fragment 都打上了一個标簽(在 mCurTransaction.add() 方法中)。
也就是說是 FragmentManager 通過 Tag 找相應的 Fragment,進而達到緩存 Fragment 的目的。如果可以找到,就不會建立新的 Fragment,Fragment 的 onCreate()、onCreateView() 等方法都不會再次調用。
是以我們需要緩存所有 Fragment 的 Tag,并且在其次,在更新資料時,使用相應的 Tag 去 FragmentManamager 中找相應的 Fragment,如果存在,就先将fragment移除掉。
2.重寫 PagerAdapter 的 getItemPosition(Object object) 方法,将傳回值固定為 POSITION_NONE。
為什麼這麼寫請看viewPager源碼:
void dataSetChanged() {
// This method only gets called if our observer is attached, so mAdapter is non-null.
final int adapterCount = mAdapter.getCount();
mExpectedAdapterCount = adapterCount;
boolean needPopulate = mItems.size() < mOffscreenPageLimit * +
&& mItems.size() < adapterCount;
int newCurrItem = mCurItem;
boolean isUpdating = false;
for (int i = ; i < mItems.size(); i++) {
final ItemInfo ii = mItems.get(i);
final int newPos = mAdapter.getItemPosition(ii.object);
if (newPos == PagerAdapter.POSITION_UNCHANGED) {
continue;
}
if (newPos == PagerAdapter.POSITION_NONE) {
mItems.remove(i);
i--;
if (!isUpdating) {
mAdapter.startUpdate(this);
isUpdating = true;
}
mAdapter.destroyItem(this, ii.position, ii.object);
needPopulate = true;
if (mCurItem == ii.position) {
// Keep the current item in the valid range
newCurrItem = Math.max(, Math.min(mCurItem, adapterCount - ));
needPopulate = true;
}
continue;
}
if (ii.position != newPos) {
if (ii.position == mCurItem) {
// Our current item changed position. Follow it.
newCurrItem = newPos;
}
ii.position = newPos;
needPopulate = true;
}
}
if (isUpdating) {
mAdapter.finishUpdate(this);
}
Collections.sort(mItems, COMPARATOR);
if (needPopulate) {
// Reset our known page widths; populate will recompute them.
final int childCount = getChildCount();
for (int i = ; i < childCount; i++) {
final View child = getChildAt(i);
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (!lp.isDecor) {
lp.widthFactor = f;
}
}
setCurrentItemInternal(newCurrItem, false, true);
requestLayout();
}
}
上面的代碼中 for 循環裡面有兩個 continue 語句,這可能是比較關鍵的代碼,幸好不用我們繼續深入了,官方給出了解釋:
Called when the host view is attempting to determine if an item’s position has changed. Returns POSITION_UNCHANGED if the position of the given item has not changed or POSITION_NONE if the item is no longer present in the adapter.The default implementation assumes that items will never change position and always returns POSITION_UNCHANGED.
大緻的意思是:
如果 Item 的位置如果沒有發生變化,則傳回 POSITION_UNCHANGED。如果傳回了 POSITION_NONE,表示該位置的 Item 已經不存在了。預設的實作是假設 Item 的位置永遠不會發生變化,而傳回 POSITION_UNCHANGED。
參考:ViewPager重新整理問題詳解