2012-03-28 76 views
22

Android平臺:3.1IllegalStateException異常:無法更改片段的容器ID

我試圖從容器移動片段到容器B.這下面的代碼來實現:

private void reattach(int newContainerId, Fragment frag, String tag) { 

     if (frag == null || !frag.isAdded() || (frag.getId() == newContainerId)) { return; } 

     final FragmentManager fm = getFragmentManager(); 
     FragmentTransaction ft = fm.beginTransaction(); 
     ft.remove(frag);  //stacco il frammento dal container A 
     ft.commit(); 
     fm.executePendingTransactions(); 

     ft = fm.beginTransaction(); 
     ft.add(newContainerId, frag, tag); //attacco il frammento sul container D 
     ft.commit(); 
     fm.executePendingTransactions(); 
    } 

當我運行的系統,我得到以下IllegalStateException異常:

03-26 00:13:14.829: E/AndroidRuntime(30090): java.lang.RuntimeException: Unable to start activity ComponentInfo{eu.areamobile.apps.sfa/eu.areamobile.apps.sfa.activity.HomeActivity}: java.lang.IllegalStateException: Can't change container ID of fragment FragmentHomeController{408202a8 id=0x7f050010 HomeController}: was 2131034128 now 2131034132 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1751) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:1767) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3117) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.ActivityThread.access$1600(ActivityThread.java:122) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1009) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.os.Handler.dispatchMessage(Handler.java:99) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.os.Looper.loop(Looper.java:132) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.ActivityThread.main(ActivityThread.java:4028) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at java.lang.reflect.Method.invokeNative(Native Method) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at java.lang.reflect.Method.invoke(Method.java:491) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at dalvik.system.NativeStart.main(Native Method) 
03-26 00:13:14.829: E/AndroidRuntime(30090): Caused by: java.lang.IllegalStateException: Can't change container ID of fragment FragmentHomeController{408202a8 id=0x7f050010 HomeController}: was 2131034128 now 2131034132 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.BackStackRecord.doAddOp(BackStackRecord.java:338) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.BackStackRecord.add(BackStackRecord.java:316) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at eu.areamobile.apps.sfa.activity.HomeActivity.reattach(HomeActivity.java:340) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at eu.areamobile.apps.sfa.activity.HomeActivity.customHideShowCreate(HomeActivity.java:253) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at eu.areamobile.apps.sfa.activity.HomeActivity.customHideShowCreate(HomeActivity.java:155) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at eu.areamobile.apps.sfa.activity.HomeActivity.onPostCreate(HomeActivity.java:66) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.Instrumentation.callActivityOnPostCreate(Instrumentation.java:1111) 
03-26 00:13:14.829: E/AndroidRuntime(30090): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:1734) 

快速調試後,我注意到,在Android 3.1 FragmentTransaction.remove不設置0正在移除片段的mContainerId,而在ICS上它正常工作。
任何建議或解決方法?

+0

喜!我也有同樣的問題。你有沒有設法在系統上使用這個工具? – Anton 2012-04-30 07:31:11

回答

0

基於Fragment Developer Guide它指出:

如果你不叫addToBackStack()當您進行交易,消除片段,則該片段被破壞時的事務被提交,用戶無法瀏覽回到它。然而,如果你調用addToBackStack()刪除片段時,則該片段被停止,將重新開始,如果用戶返回

所以我假設你的FRAG對象被調用後銷燬

ft.remove(frag);  //stacco il frammento dal container A 
ft.commit(); 

但你說它在ICS中有效,這很奇怪。你爲什麼不嘗試replace方法,看看會發生什麼?

希望這有助於...

0

嘗試使用替代()method..If不是不使用犯兩次。

0

我需要後來爲原型的東西,如果這沒有幫助,但有一件事我會嘗試這麼做並不是作爲單獨的交易,因爲即使你打電話executePendingTransactions()它不應該是一個阻塞調用,因此,儘管它正在做它的事情,其餘的代碼將開始執行,其中包括試圖將Fragment添加到一個新的容器,而它可能仍然存在於它的當前容器中。

由於您是通過單一方法完成所有這些操作,因此您可以嘗試執行刪除並添加一個FragmentTransaction,以便您知道它將按照您希望的順序執行,並讓您知道Fragment何時從一個容器中刪除,並且可以將其添加到新容器中,然後進行提交。否則,如果你真的想把它作爲兩個單獨的事務來處理,基本Fragment類有一個isRemoving()方法,它會告訴你Fragment是否被從它的容器中移除,並且你可以等到它變成false以便知道它是已被刪除。也就是說,如果現在一個事務在執行之前依賴於另一個事務,那麼您就開始引入同步問題了,而且我沒有看到任何理由不僅僅將刪除和添加爲單個事務。

謝謝, 大衛

+0

我試過了 - 它不起作用。 – 2013-11-13 01:03:42

18

我對這個問題的解決方案是,同時保持自己的狀態重新創建片段:

FragmentTransaction ft = mFragmentManager.beginTransaction(); 
ft.remove(old); 
Fragment newInstance = recreateFragment(old); 
ft.add(R.id.new_container, newInstance); 
ft.commit(); 

用下面的輔助函數:

private Fragment recreateFragment(Fragment f) 
    { 
     try { 
      Fragment.SavedState savedState = mFragmentManager.saveFragmentInstanceState(f); 

      Fragment newInstance = f.getClass().newInstance(); 
      newInstance.setInitialSavedState(savedState); 

      return newInstance; 
     } 
     catch (Exception e) // InstantiationException, IllegalAccessException 
     { 
      throw new RuntimeException("Cannot reinstantiate fragment " + f.getClass().getName(), e); 
     } 
    } 

它爲我工作,至少在最新的支持庫(r11),儘管我還沒有測試很多。

額外的成本是實例化片段兩次。

+0

我得到實例化異常。如何解決這個問題。? – 2015-05-25 10:00:35

+0

@RethinavelPillai如果沒有細節,這是不可能的。 – sstn 2015-05-26 07:10:34

+0

當我可以粘貼此代碼,如果設備旋轉? – Tomas 2018-01-22 09:59:15

12

在嘗試了類似問題的所有答案之後,看起來我已經找到了一種方法來實現這個訣竅。

第一個問題 - 你真的要提交試圖片段添加到另一個容器之前執行刪除事務。感謝那去nave's answer

但這不是每次都有效。第二個問題是後退堆棧。它以某種方式阻止交易。

所以完整的代碼,這對我的作品的樣子:

manager.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); 
manager.beginTransaction().remove(detailFragment).commit(); 
manager.executePendingTransactions(); 
manager.beginTransaction() 
    .replace(R.id.content, masterFragment, masterTag) 
    .add(R.id.detail, detailFragment, activeTag) 
    .commit();    
1

我加入片段第二次奏效了,我之前也有類似的問題,並呼籲manager.executePendingTransactions()。 謝謝!

0

除了糟糕的編程原則之外,是否有任何有效的原因,您希望在整個活動或兩個活動中保留對現有片段的引用?而不是保存片段的狀態並簡單地重新創建它?

使用

我創建片段:

public static ContactsFragment mContactsFragment; 

    public static ContactsFragment getInstance() { 
    if (mContactsFragment == null) { 
     mContactsFragment = new ContactsFragment(); 
    } 
    return mContactsFragment; 
} 

我做這個,因爲我學到的Android,但不明白爲什麼我會想,儘管它在每個人的例子是靜態參考我的片段。最初我以爲是靜態地引用一個Context,但是當你創建一個新的片段並且無論如何保存一個上下文時,你總是可以設置它,這往往會導致以後的奇怪錯誤。

你爲什麼不:

public static ContactsFragment newInstance(Context c) { 
    ContactsFragment mContactsFragment = new ContactsFragment(); 
    mContext = c; 
    return mContactsFragment; 
} 

,並只需保存你的片段做,當你再次需要重新創建任何工作? 也許我可以想象,如果你正在創建100+(新聞應用程序?),你可能不想每次都創建它們,而是將它們存儲在內存中?任何人?

0

所以,我有同樣的問題,並以不同的方式解決問題(至少有點)。我的應用程序有4個片段,當我的設備處於橫向模式時,我需要管理這些片段。

讓我解釋一下我的應用程序和解決方案:

我的應用 我在我的應用程序4個片段: 1 - 食譜(含配方列表) 2 - 配料(名單從選擇配方成分) 3 - 步(步列表) 4 - 播放器(這就是所謂的以播放視頻)

在肖像模式我沒有問題的片段,一切工作正常!

景觀 我的活動有2個集裝箱爲我的片段,一個在左側至極包含我的食譜列表,另一個在右側。右側的容器會發生變化。

我有這個問題,什麼時候,肖像模式,我的片段顯示,當我旋轉我的設備,我想要顯示的細節容器相同的片段。 而那就是我所有的問題開始。

我的解決方案

  1. 保存的最後一個片段顯示在一個變量;
  2. 保存狀態片段onSavedInstance;
  3. 在onCreate中檢索片段並顯示最後一個片段。

所以,要更加especific我創造了這個要點,幫助 https://gist.github.com/jillesRagonha/4c184165b92fdf026ab3cd033b64b1bf

希望這有助於人:d

相關問題