IllegalStateException: No se puede cambiar ID de contenedor del fragmento

Plataforma Android: 3.1

Estoy tratando de mover un fragmento de un contenedor A a un contenedor B. Aquí sigue el código para lograr esto:

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

Cuando ejecuto el sistema, obtengo la siguiente 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) 

Después de una depuración rápida, me di cuenta de que en Android 3.1 el FragmentTransaction.remove no establece en 0 el mContainerId del fragmento que se está eliminando, mientras que en ICS funciona correctamente.
¿Alguna sugerencia o solución?

Mi solución a este problema es volver a crear el fragmento manteniendo su estado:

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

Con la siguiente función auxiliar:

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

Funciona para mí, al menos con la última biblioteca de soporte (r11), aunque todavía no he probado mucho.

El coste adicional es instanciar el fragmento dos veces.

Después de intentar todas las respuestas de preguntas similares, parece que he encontrado una manera de hacer el truco.

Primer problema – realmente tienes que confirmar y ejecutar eliminar la transacción antes de intentar agregar fragmento a otro contenedor. Gracias por eso va a la respuesta de la nave

Pero esto no funciona cada vez. La segunda cuestión es una pila trasera. De alguna manera bloquea la transacción.

Así que el código completo, que funciona para mí se parece a:

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

Tuve un problema similar y llamar a manager.executePendingTransactions () antes de agregar el fragmento por segunda vez hizo el truco para mí. ¡Gracias!

Basado en la Guía del Desarrollador de Fragmentos que dice:

Si no llama a addToBackStack () cuando realiza una transacción que elimina un fragmento, ese fragmento se destruye cuando se compromete la transacción y el usuario no puede volver a navegar. Considerando que, si llama a addToBackStack () al eliminar un fragmento, el fragmento se detiene y se reanudará si el usuario vuelve a navegar

Asumo que su objeto frag se destruye después de las llamadas a

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

Pero usted dice que funciona en ICS y esto es extraño. ¿Por qué no probar el método de reemplazo y ver qué pasa?

Espero que esto ayude…

Intenta usar el método replace () .. Si no usas commit dos veces.

Necesitaré prototipo algo más adelante si esto no ayuda pero una cosa que intentaría no está haciendo esto como transacciones separadas porque aunque usted llama executePendingTransactions () no debe ser una llamada de bloqueo, así que mientras que hace su cosa El resto de su código comenzará a ejecutarse, lo que incluye tratar de agregar el fragmento a un nuevo contenedor mientras aún podría existir en su contenedor actual.

Puesto que usted está haciendo todo esto desde un solo método, puede intentar hacer la eliminación y agregar en un único FragmentTransaction, para que sepa que se ejecutará en el orden en que lo desea, y para que sepa cuando el fragmento Ha sido eliminado de un contenedor y puede agregarlo a uno nuevo y luego confirmarlo. De lo contrario, si realmente quieres hacerlo como dos transacciones separadas, la clase Fragmento base tiene un método isRemoving () que te dirá si el Fragmento está siendo eliminado de su contenedor y puedes esperar hasta que se convierta en falso para que sepas que es Sido eliminado. Dicho esto, usted está comenzando a introducir problemas de sincronización si ahora una transacción depende de otra antes de que pueda ejecutar, y no veo ninguna razón no sólo para eliminar y agregar como una sola transacción.

Gracias, David

¿Hay alguna razón válida aparte de pobres directores de programación, que desea mantener una referencia a un fragmento existente a través de una actividad o dos de todos modos? En lugar de salvar el estado del fragmento y simplemente recrearlo?

Estaba creando fragmentos usando:

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

He hecho esto desde que aprendí Android, pero no puedo ver por qué me gustaría referirme a mi fragmento estáticamente a pesar de que está en todos los ejemplos. Originalmente pensé que era referirse a un contexto de forma estática, pero siempre se puede establecer esto cuando se crea un nuevo fragmento y guardar un contexto de todos modos a menudo conduce a errores extra más tarde de todos modos.

¿Por qué no:

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

Y simplemente guardar cualquier trabajo que haya hecho en el fragmento y volver a crearlo cuando lo necesite de nuevo? Tal vez me puedo imaginar si usted estaba creando 100 + (app de noticias?) Es posible que no quieren tener que crear cada vez, en lugar de almacenar en la memoria? ¿Nadie?

  • Fragmentos anidados utilizando la biblioteca de soporte v4 revisión 11
  • Deshabilitar el desplazamiento entre las pestañas en FragmentActivity
  • Problemas de memoria en fragmentos que muestran imágenes
  • Cómo comprometer Fragment transacciones en FragmentPagerAdapter en onLoadFinish método de devolución de llamada?
  • Cómo utilizar diferentes onKeyDown en fragmento de una misma actividad
  • Método de findPreference () no obsoleto? - Android
  • Fragmentos que desaparecen después de la aplicación no se ha utilizado durante pocas horas
  • Actividad de preferencias de Android IllegalArgumentException: fragmento no válido para esta actividad
  • Android: tabhost en un fragmento, w / fragmentos como fichas
  • ¿Cuál es la forma correcta de compartir datos entre diferentes Actividades o Fragmentos?
  • Menú de opciones de la barra de acciones de FragmentActivity
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.