IllegalStateException:无法更改Fragment的容器ID

Android平台:3.1

我想从一个容器A的一个片段移动到一个容器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) 

快速debugging之后,我注意到在Android 3.1上,FragmentTransaction.remove没有设置为0的被删除片段的mContainerId,而在ICS上,它工作正常。
任何build议或解决方法?

Related of "IllegalStateException:无法更改Fragment的容器ID"

我对这个问题的解决scheme是重新创build片段,同时保持其状态:

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

使用以下帮助器function:

 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); } } 

它适用于我,至less在最新的支持库(r11),尽pipe我还没有testing过。

额外的成本是两次实例化片段。

在尝试所有类似问题的答案后,看起来像我已经find了一个办法来做这个伎俩。

第一个问题 – 在尝试将碎片添加到另一个容器之前,您实际上必须提交执行删除事务。 谢谢你的回答

但是这不是每次都有效。 第二个问题是后退堆栈。 它以某种方式阻止交易。

所以完整的代码,对我来说很像:

 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(); 

我有一个类似的问题,并调用manager.executePendingTransactions()第二次添加片段之前做了我的伎俩。 谢谢!

基于Fragment开发者指南 ,它指出:

如果在执行删除片段的事务时不调用addToBackStack(),则在提交事务并且用户无法导航到片段时,该片段将被销毁。 而如果在删除片段时确实调用了addToBackStack(),则片段将停止,并在用户返回时恢复

所以我假设你的frag对象在调用之后被销毁

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

但是你说它在ICS中起作用,这很奇怪。 你为什么不尝试replace方法,看看会发生什么?

希望这可以帮助…

尝试使用replace()方法..如果不使用提交两次。

如果这不起作用,我将需要做一些原型,但是有一点我不会做,因为即使你调用executePendingTransactions(),它也不应该是一个阻塞调用,所以当它做它的事情剩下的代码将开始执行,其中包括尝试将Fragment添加到新的容器中,同时它仍然存在于其当前容器中。

既然你是从一个单一的方法来完成这些工作,你可以尝试去除并添加一个FragmentTransaction,这样你就知道它将按照你想要的顺序执行,这样你就知道了什么时候片段已经从一个容器中删除,可以添加到一个新的,然后提交。 否则,如果你真的想把它作为两个单独的事务来处理,基本的片段类有一个isRemoving()方法,它会告诉你Fragment是否被从它的容器中移除,并且你可以等到它变成false,这样你才知道它是已被删除。 也就是说,如果现在一个事务依赖另一个事务执行之前就开始引入同步问题,并且我不认为有任何理由不仅仅是将其作为单个事务进行删除和添加。

谢谢,大卫

除了糟糕的编程原则之外,是否有任何有效的理由,您是否希望在一个或两个活动中保留对现有片段的引用? 而不是保存片段的状态,并简单地重新创build?

我正在创build片段使用:

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

自从我学习Android以来,我已经完成了这个工作,但是却不明白为什么我想静态地引用我的片段,尽pipe它在每个人的例子中。 最初我以为是静态地引用一个Context,但是当你创build一个新的片断并且无论如何保存一个上下文的时候,你总是可以设置这个常常会导致一些奇怪的错误。

你为什么不呢:

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

并简单地保存你在片段上做的任何工作,并在需要时重新创build它? 也许我可以想象,如果你正在创build100+(新闻应用程序?),你可能不想每次都创build它们,而是将它们存储在内存中? 任何人?