2012-03-28 71 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

相关问题