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)
经过快速调试后,我注意到在Android 3.1上FragmentTransaction.remove没有将要删除的片段的mContainerId设置为0,而在ICS上它可以正常工作。
有任何建议或解决方法吗?
答案 0 :(得分:19)
我解决这个问题的方法是在保持状态的同时重新创建片段:
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),虽然我还没有测试过多。
额外的成本是将片段实例化两次。
答案 1 :(得分:16)
第一个问题 - 在尝试将片段添加到另一个容器之前,您必须提交并执行删除事务。感谢您访问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();
答案 2 :(得分:2)
我有类似的问题,并且在第二次添加片段之前调用manager.executePendingTransactions()为我做了诀窍。 谢谢!
答案 3 :(得分:1)
尝试多次添加相同的fragment
时可能会发生这种情况。
public void populateFragments() {
Fragment fragment = new Fragment();
//fragment is added for the first time
addFragment(fragment);
// fragment is added for the second time
// This call will be responsible for the issue. The
addFragment(fragment);
}
public void addFragment(Fragment fragment) {
FrameLayout frameLayout = AppView.createFrameLayout(context);
view.addView(frameLayout);
getSupportFragmentManager().beginTransaction().add(frameLayout.getId(), fragment).commit();
}
答案 4 :(得分:0)
基于Fragment Developer Guide,它声明:
如果在执行删除片段的事务时未调用addToBackStack(),则在提交事务并且用户无法导航回到该片段时,该片段将被销毁。然而,如果您在删除片段时调用addToBackStack(),则片段将停止,并且如果用户导航回来将恢复该片段
所以我假设在调用
之后你的frag对象被破坏了ft.remove(frag); //stacco il frammento dal container A
ft.commit();
但你说它在ICS中有效,这很奇怪。为什么不试试replace方法,看看会发生什么?
希望这会有所帮助......
答案 5 :(得分:0)
尝试使用replace()方法。如果不使用提交两次。
答案 6 :(得分:0)
如果这没有帮助,我需要稍后原型化,但我会尝试的一件事就是不做单独的事务,因为即使你调用executePendingTransactions()它也不应该是一个阻塞调用,所以虽然它是做其余的代码将开始执行,包括尝试将Fragment添加到新容器,而它可能仍然存在于当前容器中。
由于您是通过单个方法完成所有这些操作,因此您可以尝试执行删除并添加一个FragmentTransaction,以便您知道它将按照您希望的顺序执行,以便您知道从一个容器中删除Fragment并将其添加到新容器然后提交时。另外,如果你真的想把它作为两个单独的事务来做,那么基础Fragment类有一个isRemoving()方法,它会告诉你是否从它的容器中删除了Fragment,你可以等到它变为false,这样你就知道它了被删除了。也就是说,如果现在一个事务在执行之前依赖于另一个事务,那么你就开始引入同步问题了,我认为没有任何理由不去删除并添加为单个事务。
谢谢, 大卫
答案 7 :(得分: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多个(新闻应用程序?),你可能不想每次创建它们,而是将它们存储在内存中?任何人吗?
答案 8 :(得分:0)
所以,我有同样的问题,并以不同的方式(至少一点点)解决了问题。我的应用程序有4个片段,我需要在设备处于横向模式时进行管理。
让我解释一下我的应用和解决方案:
我的应用 我的应用程序中有4个片段: 1 - 食谱(包含食谱列表) 2 - 成分(选择配方的成分列表) 3 - 步骤(步骤列表) 4 - 播放器(一个叫做播放视频的片段)
在纵向模式下我没有问题,一切正常!
<强>风景强> 我的活动有两个容器用于我的碎片,一个在左侧,包含我的食谱列表,另一个在右侧。右侧的这个容器将会发生变化。
我有这个问题,在肖像模式下,我的片段正在显示,当我旋转我的设备时,我想要显示的相同片段转到详细信息容器。 这就是我所有问题的开始。
我的解决方案
所以,更具体的是我创造了这个要点来帮助 https://gist.github.com/jillesRagonha/4c184165b92fdf026ab3cd033b64b1bf
希望这有助于任何人:D
答案 9 :(得分:0)
我发现问题是我需要两个FragmentTransactions。我试图在同一个目录上两次执行remove()和commit()。
// create new fragment
MyFragment myNewFragment = MyFragment.newInstance(this, data, true);
FragmentManager fm = this.getSupportFragmentManager();
// clear the back stack
fm.popBackStackImmediate(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
//fragmentTransaction is created for the removal of the old
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.remove(myOldFragment);
fragmentTransaction.commit();
fm.executePendingTransactions();
// fragmentTransaction2 is created to add the new one
FragmentTransaction fragmentTransaction2 = fm.beginTransaction();
fragmentTransaction2.add(R.id.fragment_frame, myNewFragment);
fragmentTransaction2.commit();
fm.executePendingTransactions();
答案 10 :(得分:0)
删除 transaction.commitAllowingStateLoss();
(如果存在)(对于之前的片段)并使用 transaction.commit();
代替。
最好为 BaseActivity/BaseFragment 中的所有片段/活动保留一个像这样的全局函数
protected void replaceMyFragment(Fragment fragment, int container_view_id, boolean addToBackStack, String tag) {
// Begin the transaction
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
// Replace the contents of the container with the new fragment
//ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
ft.replace(container_view_id, fragment, tag);
// or ft.add(R.id.your_placeholder, new FooFragment());
if (addToBackStack) {
ft.addToBackStack(tag);
}
// Complete the changes added above
ft.commit();
}
然后这样称呼它们:
replaceMyFragment(new YourFragment(),R.id.your_container,false,fragment.getTag());
在您的活动/片段中。