欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

fragment中的add和replace方法的區(qū)別淺析

 更新時間:2017年01月19日 09:57:35   作者:shw372029857  
使用 FragmentTransaction 的時候,它提供了這樣兩個方法,一個 add , 一個 replace ,對這兩個方法的區(qū)別一直有點疑惑。下面小編通過本文給大家簡單介紹下fragment中的add和replace方法的區(qū)別,一起看看吧

使用 FragmentTransaction 的時候,它提供了這樣兩個方法,一個 add , 一個 replace ,對這兩個方法的區(qū)別一直有點疑惑。

我覺得使用 add 的話,在按返回鍵應該是回退到上一個 Fragment,而使用 replace 的話,那個別 replace 的就已經不存在了,所以就不會回退了。但事實不是這樣子的。add 和 replace 影響的只是界面,而控制回退的,是事務。

public abstract FragmentTransaction add (int containerViewId, Fragment fragment, String tag)
Add a fragment to the activity state. This fragment may optionally also have its view (if Fragment.onCreateView returns non-null) into a Container view of the activity.

add 是把一個fragment添加到一個容器 container 里。

public abstract FragmentTransaction replace (int containerViewId, Fragment fragment, String tag)
Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.

replace 是先remove掉相同id的所有fragment,然后在add當前的這個fragment。

在大部分情況下,這兩個的表現(xiàn)基本相同。因為,一般,咱們會使用一個FrameLayout來當容器,而每個Fragment被add 或者 replace 到這個FrameLayout的時候,都是顯示在最上層的。所以你看到的界面都是一樣的。但是,使用add的情況下,這個FrameLayout其實有2層,多層肯定要比一層的來得浪費,所以還是推薦使用replace。當然有時候還是需要使用add的。比如要實現(xiàn)輪播圖的效果,每個輪播圖都是一個獨立的Fragment,而他的容器FrameLayout需要add多個Fragment,這樣他就可以根據(jù)提供的邏輯進行輪播了。

而至于返回鍵的時候,這個跟事務有關,跟使用add還是replace沒有任何關系。

2015.08.04 更新。

發(fā)現(xiàn)這篇博文被搜索得挺多的,上面是分析是在官方文檔上的基礎上加上一些個人的猜測,為了避免誤人子弟,下面從代碼實現(xiàn)的角度做了些分析。希望能幫到大家,也煩請大家在轉載的同時注明出處,畢竟寫這么一篇博文確實很不容易(binkery)。

Android Fragment 和 FragmentManager 的代碼分析 這篇文章也是從代碼的角度分析了 FragmentManager 的工作機制。

FragmentManager 是一個抽象類,實現(xiàn)類是 FragmentManagerImpl ,跟 FragmentManager 在同一個類文件里。FragmentTransaction 也是一個抽象類,具體實現(xiàn)是 BackStackRecord 。BackStackRecord 其實是一個封裝了一個隊列。咱們看 add 方法和 replace 方法。

add 方法和 replace 方法都是把一個操作 OP_XX 放入到隊列里,Op 是其內部封裝的一個操作的類。在 BackStackRecord 的 run 方法里,每次會從隊列的頭(mHead)獲取一個操作 Op ,如果 Op 操作是 add ,則調用 FragmentManager 的 addFragment() 方法,如果 Op 操作是 replace ,則先調用 FragmentManager 的 removeFragment() 方法,然后再調用 addFragment() 方法。

下面是 add 方法。

public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
 doAddOp(containerViewId, fragment, tag, OP_ADD);
 return this;
}

下面是 replace 方法。

public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
 if (containerViewId == 0) {
 throw new IllegalArgumentException("Must use non-zero containerViewId");
 }
 doAddOp(containerViewId, fragment, tag, OP_REPLACE);
 return this;
}

add 和 replace 方法都是調用的 doAddOp 方法。也就是把一個操作 Op 添加到隊列。

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
 fragment.mFragmentManager = mManager;
 if (tag != null) {
 if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
 throw new IllegalStateException("Can't change tag of fragment "
  + fragment + ": was " + fragment.mTag
  + " now " + tag);
 }
 fragment.mTag = tag;
 }
 if (containerViewId != 0) {
 if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
 throw new IllegalStateException("Can't change container ID of fragment "
  + fragment + ": was " + fragment.mFragmentId
  + " now " + containerViewId);
 }
 fragment.mContainerId = fragment.mFragmentId = containerViewId;
 }
 Op op = new Op();
 op.cmd = opcmd;
 op.fragment = fragment;
 addOp(op);
}

run 方法才是真正執(zhí)行的方法。什么時候執(zhí)行先不考慮,只需要知道一系列的操作會一次執(zhí)行,而不是一個操作執(zhí)行一次。
run 方法有點大,就看一下 while 循環(huán)開始和結束的時候,以及 switch case 里 OP_ADD 和 OP_REPLACE 分支就可以了。

public void run() {
 if (FragmentManagerImpl.DEBUG) {
 Log.v(TAG, "Run: " + this);
 }
 if (mAddToBackStack) {
 if (mIndex < 0) {
 throw new IllegalStateException("addToBackStack() called after commit()");
 }
 }
 bumpBackStackNesting(1);
 SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
 SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
 calculateFragments(firstOutFragments, lastInFragments);
 beginTransition(firstOutFragments, lastInFragments, false);
 // 獲取隊列的頭
 Op op = mHead;
 while (op != null) {
 switch (op.cmd) {
 case OP_ADD: {
 Fragment f = op.fragment;
 f.mNextAnim = op.enterAnim;
 mManager.addFragment(f, false);//添加
 }
 break;
 case OP_REPLACE: {
 Fragment f = op.fragment;
 if (mManager.mAdded != null) {
  for (int i = 0; i < mManager.mAdded.size(); i++) {
  Fragment old = mManager.mAdded.get(i);
  if (FragmentManagerImpl.DEBUG) {
  Log.v(TAG,
   "OP_REPLACE: adding=" + f + " old=" + old);
  }
  if (f == null || old.mContainerId == f.mContainerId) {
  if (old == f) {
  op.fragment = f = null;
  } else {
  if (op.removed == null) {
   op.removed = new ArrayList<Fragment>();
  }
  op.removed.add(old);
  old.mNextAnim = op.exitAnim;
  if (mAddToBackStack) {
   old.mBackStackNesting += 1;
   if (FragmentManagerImpl.DEBUG) {
   Log.v(TAG, "Bump nesting of "
   + old + " to " + old.mBackStackNesting);
   }
  }
  mManager.removeFragment(old, mTransition, mTransitionStyle);//刪除
  }
  }
  }
 }
 if (f != null) {
  f.mNextAnim = op.enterAnim;
  mManager.addFragment(f, false);//添加
 }
 }
 break;
 case OP_REMOVE: {
 Fragment f = op.fragment;
 f.mNextAnim = op.exitAnim;
 mManager.removeFragment(f, mTransition, mTransitionStyle);
 }
 break;
 case OP_HIDE: {
 Fragment f = op.fragment;
 f.mNextAnim = op.exitAnim;
 mManager.hideFragment(f, mTransition, mTransitionStyle);
 }
 break;
 case OP_SHOW: {
 Fragment f = op.fragment;
 f.mNextAnim = op.enterAnim;
 mManager.showFragment(f, mTransition, mTransitionStyle);
 }
 break;
 case OP_DETACH: {
 Fragment f = op.fragment;
 f.mNextAnim = op.exitAnim;
 mManager.detachFragment(f, mTransition, mTransitionStyle);
 }
 break;
 case OP_ATTACH: {
 Fragment f = op.fragment;
 f.mNextAnim = op.enterAnim;
 mManager.attachFragment(f, mTransition, mTransitionStyle);
 }
 break;
 default: {
 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
 }
 }
 op = op.next;//隊列的下一個
 }
 mManager.moveToState(mManager.mCurState, mTransition,
 mTransitionStyle, true);
 if (mAddToBackStack) {
 mManager.addBackStackState(this);
 }
}

BackStackRecord 的構造器里參數(shù)列表里有一個 FragmentManager ,所有 BackStackRecord 其實是有一個 FragmentManager 的引用的,BackStackRecord 可以直接調用 FragmentManager 的 addFragment 方法。

下面是 FragmentManager 的 addFragment() 方法,每次 add 一個 Fragment,F(xiàn)ragment 對象都會被放入到 mAdded 的容器里。

public void addFragment(Fragment fragment, boolean moveToStateNow) {
 if (mAdded == null) {
 mAdded = new ArrayList<Fragment>();
 }
 if (DEBUG) Log.v(TAG, "add: " + fragment);
 makeActive(fragment);
 if (!fragment.mDetached) {
 if (mAdded.contains(fragment)) {
 throw new IllegalStateException("Fragment already added: " + fragment);
 }
 mAdded.add(fragment);
 fragment.mAdded = true;
 fragment.mRemoving = false;
 if (fragment.mHasMenu && fragment.mMenuVisible) {
 mNeedMenuInvalidate = true;
 }
 if (moveToStateNow) {
 moveToState(fragment);
 }
 }
}

有時候,咱們 add Fragment A, 然后 add Fragment B,B 把 A 都覆蓋了,點擊菜單的時候 A 和 B 的菜單選項都出來了,這是為什么?原因在下面。當在創(chuàng)建 OptionsMenu 的時候,F(xiàn)ragmentManager 遍歷了 mAdded 容器,所以 A 和 B 的菜單都被添加進來了。也就是說使用 add 的方式,雖然 B 把 A 覆蓋住了,但是 A 還是存活的,而且是活動著的。

public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
 boolean show = false;
 ArrayList<Fragment> newMenus = null;
 if (mAdded != null) {
 for (int i=0; i<mAdded.size(); i++) {
 Fragment f = mAdded.get(i);
 if (f != null) {
 if (f.performCreateOptionsMenu(menu, inflater)) {
  show = true;
  if (newMenus == null) {
  newMenus = new ArrayList<Fragment>();
  }
  newMenus.add(f);
 }
 }
 }
 }
 if (mCreatedMenus != null) {
 for (int i=0; i<mCreatedMenus.size(); i++) {
 Fragment f = mCreatedMenus.get(i);
 if (newMenus == null || !newMenus.contains(f)) {
 f.onDestroyOptionsMenu();
 }
 }
 }
 mCreatedMenus = newMenus;
 return show;
}

小結:fragment中的add和replace方法的區(qū)別

使用add方法時,需要考慮fragment引用被清空的情況。

        使用add方法add到activity里面的fragment的對象并不會被銷毀。也就是它任然在activity中存在,只是應用被置為null而已。此時如果重新為fragment賦值,其hide方法和show方法都不會生效。如果這種情況下,一個activity中有多個fragment,很可能出現(xiàn)多個fragment層疊而不能正常的顯示或者隱藏。

       使用add方法使用的fragment的優(yōu)點在于它占用內存資源少,通過replace方法使用fragment占用資源雖然會多一些,但是不存在add方法的bug。

      所以開發(fā)的時候,盡量處理好add方法可能引起的bug。

     fragment還要處理好commit和transaction.commitAllowingStateLoss()兩個方法。

以上所述是小編給大家介紹的fragment中的add和replace方法的區(qū)別淺析,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回復大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關文章

最新評論