问题
I have a screen where I want to swap two fragments by hiding and showing (not replacing them) when certain views are clicked. Here is my code:
...
private Fragment currentFragment;
...
private void swapFragment(Fragment fragment) {
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
if (currentFragment != null && !currentFragment.isHidden()) {
ft.hide(currentFragment);
}
if (!fragment.isAdded()) {
ft.add(R.id.fragment_holder, fragment);
} else {
ft.show(fragment);
}
ft.commit();
currentFragment = fragment;
}
...
R.id.fragment_holder
is a FrameLayout. I do not declare any <fragment>
tags in my layout. Originally my swap method was simply this...
getChildFragmentManager().beginTransaction().replace(R.id.fragment_holder, fragment).commit();
...but for reasons I won't get into here I want to do show/hide instead of replace.
Logically this should be pretty straightforward: if there's a fragment showing, hide it first. If it's the first time showing this fragment, add it, otherwise show it. Then save the current fragment.
The problem is on first launch, this crashed with an IllegalStateException
stating the fragment has already been added.
11-12 08:36:49.098: E/AndroidRuntime(27293): FATAL EXCEPTION: main
11-12 08:36:49.098: E/AndroidRuntime(27293): java.lang.RuntimeException: Unable to start activity ComponentInfo{com.my.package/com.my.package.activities.MainActivity}: java.lang.IllegalStateException: Fragment already added: ContactFragment{42263030 #0 id=0x7f0a006d}
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2324)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2374)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.app.ActivityThread.access$600(ActivityThread.java:154)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1248)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.os.Handler.dispatchMessage(Handler.java:99)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.os.Looper.loop(Looper.java:137)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.app.ActivityThread.main(ActivityThread.java:5242)
11-12 08:36:49.098: E/AndroidRuntime(27293): at java.lang.reflect.Method.invokeNative(Native Method)
11-12 08:36:49.098: E/AndroidRuntime(27293): at java.lang.reflect.Method.invoke(Method.java:511)
11-12 08:36:49.098: E/AndroidRuntime(27293): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:799)
11-12 08:36:49.098: E/AndroidRuntime(27293): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:566)
11-12 08:36:49.098: E/AndroidRuntime(27293): at dalvik.system.NativeStart.main(Native Method)
11-12 08:36:49.098: E/AndroidRuntime(27293): Caused by: java.lang.IllegalStateException: Fragment already added: ContactFragment{42263030 #0 id=0x7f0a006d}
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.FragmentManagerImpl.addFragment(FragmentManager.java:1175)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.BackStackRecord.run(BackStackRecord.java:616)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1460)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.Fragment.performStart(Fragment.java:1499)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:957)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1104)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.FragmentManagerImpl.moveToState(FragmentManager.java:1086)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.FragmentManagerImpl.dispatchStart(FragmentManager.java:1882)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.support.v4.app.FragmentActivity.onStart(FragmentActivity.java:573)
11-12 08:36:49.098: E/AndroidRuntime(27293): at com.my.package.activities.MainActivity.onStart(MainActivity.java:252)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1164)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.app.Activity.performStart(Activity.java:5233)
11-12 08:36:49.098: E/AndroidRuntime(27293): at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2287)
11-12 08:36:49.098: E/AndroidRuntime(27293): ... 11 more
回答1:
So it turns out my swapping logic works just fine. The problem is actually something with RadioGroup
in which it's OnCheckChangedListener
can be called twice for a single check change among its child RadioButton
s. This produces two FragmentTransaction
s, the second of which causes the IllegalStateException because it tries to add an already added fragment.
The solution for me was to add
if (fragment == currentFragment) return;
at the beginning of swapFragment()
.
来源:https://stackoverflow.com/questions/19935400/illegalstateexception-with-android-fragments