2013-11-12 5 views
0

У меня есть экран, где я хочу поменять местами два фрагмента, скрыв и показывая (не, заменяя их) при нажатии определенных видов. Вот мой код:IllegalStateException с Android Fragments

... 
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 является FrameLayout. Я не объявляю теги <fragment> в своем макете. Первоначально мой метод своп был просто это ...

getChildFragmentManager().beginTransaction().replace(R.id.fragment_holder, fragment).commit(); 

... но по причинам, я не буду вдаваться в здесь я хочу показать/скрыть вместо замены.

Логично, что это должно быть довольно просто: если есть фрагмент, скройте его первым. Если это первый раз, показывая этот фрагмент, добавьте его, иначе покажите его. Затем сохраните текущий фрагмент.

Проблема заключается в первом запуске, это разбилось с IllegalStateException, в котором говорится, что фрагмент уже добавлен.

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 
+0

Не могли бы вы показать нам метод 'onStart' вашего класса' Activity'? – JuniKim

+0

Вы должны заменить или удалить фрагмент перед добавлением нового фрагмента. – Subbu

+0

@Homosapiens: Тогда почему существуют API-интерфейсы show and hide? – Karakuri

ответ

0

Так что получается, что моя логика обмена работает нормально. Проблема на самом деле - это что-то с RadioGroup, в котором его OnCheckChangedListener можно вызывать дважды для одного изменения чека между его дочерним элементом RadioButton. Это создает два FragmentTransaction s, второй из которых вызывает исключение IllegalStateException, поскольку он пытается добавить уже добавленный фрагмент.

Решение для меня было добавить

if (fragment == currentFragment) return; 

в начале swapFragment().

+0

Возможно, вам захочется предпочесть 'if (фрагмент == null || frag.equals (currentFragment))'. Это может иметь больше смысла сравнивать на основе содержимого, а не указателя. – Risadinha