2012-03-28 11 views
22

plataforma Android: 3.1IllegalStateException: No se puede cambiar de ID de contenedor del fragmento

Estoy tratando de mover un fragmento de un recipiente de A a un recipiente B. Aquí sigue el código de 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, me sale el 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, he notado que en Android 3.1 del FragmentTransaction.remove no se pone a 0 el mContainerId del fragmento que se está eliminando, mientras que en ICS funciona correctamente.
¿Alguna sugerencia o solución alternativa?

+0

Hola! Tengo el mismo problema. ¿Ha logrado que esto funcione en sistemas anteriores? – Anton

Respuesta

0

Basado en Fragment Developer Guide el que afirma:

Si usted no llama addToBackStack() cuando se realiza una transacción que elimina un fragmento, entonces ese fragmento se destruye cuando la transacción se confirma y el usuario no puede navegar de regreso a ella. Considerando que, si lo hace llamar addToBackStack() al retirar un fragmento, a continuación, el fragmento se detiene y se reanudará si el usuario navega hacia atrás

Así que supongo que su objeto de fragmentación se destruye después de las llamadas a

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

Pero dices que funciona en ICS y esto es extraño. ¿Por qué no pruebas el método replace y ves lo que sucede?

Espero que esto ayude ...

0

Intente utilizar replace() no method..If no utilice cometer dos veces.

0

Voy a tener que prototipar algo más adelante si esto no ayuda, pero una cosa que trataría es de no hacer esto como transacciones separadas porque aunque llame a executePendingTransactions() no debería ser una llamada de bloqueo, así que mientras está haciendo lo suyo, el resto de su código comenzará a ejecutarse, lo que incluye intentar agregar el Fragmento a un nuevo contenedor mientras aún existe en su contenedor actual.

Dado que está haciendo todo esto desde un solo método, puede intentar eliminar y agregar una FragmentTransaction única, para que sepa que se ejecutará en el orden en que lo desee, y para que pueda saber cuándo se ha eliminado el Fragmento 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 de Fragmento base tiene un método isRemoving() que te dirá si el Fragmento se está eliminando de su contenedor y puedes esperar hasta que sea falso para que sepas que es sido eliminado Dicho esto, estás comenzando a introducir problemas de sincronización si ahora una transacción depende de otra antes de que se pueda ejecutar, y no veo ninguna razón para no solo eliminarla y agregarla como una sola transacción.

Gracias, David

+0

Lo he intentado, no funciona. –

18

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

Se funciona para mí, al menos con la última biblioteca de soporte (r11), aunque todavía no hice muchas pruebas.

El costo adicional es instanciar el fragmento dos veces.

+0

Recibo una excepción de ejemplificación. ¿Cómo resolver eso? –

+0

@RethinavelPillai Eso es imposible de decir sin detalles. – sstn

+0

¿Cuándo puedo pegar este código si la rotación del dispositivo? – Tomas

12

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

Primer número - que realmente tiene que cometen y Ejecutar transacción quitar antes de intentar añadir un fragmento a otro contenedor. Gracias por eso va a nave's answer

Pero esto no funciona todo el tiempo. El segundo problema es una pila de respaldo. De alguna manera bloquea la transacción.

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

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

tuve un problema similar y manager.executePendingTransactions llamada() antes de añadir el fragmento por segunda vez hizo el truco para mí. Gracias!

0

¿Hay alguna razón válida aparte de los directores de programación deficientes, que desea mantener una referencia a un fragmento existente a través de una actividad o dos de todos modos? ¿En lugar de guardar 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 hacer referencia a mi fragmento de forma estática a pesar de estar en los ejemplos de cada uno. Originalmente, pensé que era para referirse a un contexto de forma estática, pero siempre se puede configurar cuando se crea un nuevo fragmento y guardar un contexto de todos modos a menudo conduce a errores extraños de todos modos.

¿Por qué no te:

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

y simplemente guardar cualquier trabajo que has hecho en el fragmento y volver a crearla cuando lo necesite de nuevo? Tal vez pueda imaginar que si creaba más de 100 (¿aplicación de noticias?) Quizás no quiera tener que crearlas cada vez, sino almacenarlas en la memoria. ¿Nadie?

0

Así que tuve el mismo problema y resolví el problema de una manera diferente (al menos un poco). Mi aplicación tiene 4 fragmentos y debo administrarla cuando mi dispositivo esté en modo paisaje.

permítanme explicar mi aplicación y la solución:

Mi Aplicación tengo 4 fragmentos en mi aplicación: 1 - Recetas (que contienen una lista de recetas) 2 - Ingredientes (una lista de ingredientes de la receta de selección) 3 - pasos (lista de pasos) 4 - jugador (un fragmento que se llama para reproducir vídeos)

en el modo vertical no tuve ningún problema, todo funciona bien!

paisaje Mi actividad tienen 2 contenedores para mis fragmentos, uno en wich lado izquierdo contiene mi lista de recetas, y otro en el lado derecho. Este contenedor en el lado derecho cambiará dinamicamente.

Tengo este problema, cuando, en modo retrato, se muestra mi fragmento, y cuando giro mi dispositivo, quiero que el mismo fragmento que se muestra vaya al contenedor de detalles. Y ahí es donde comenzaron todos mis problemas.

mi solución

  1. Guardar el último fragmento mostró en una variable;
  2. Guarde el fragmento de estado enSavedInstance;
  3. En onCreate recupera el fragmento y el último fragmento mostrado.

lo tanto, para ser más especific creé esta GIST para ayudar https://gist.github.com/jillesRagonha/4c184165b92fdf026ab3cd033b64b1bf

esperanza esta ayuda a nadie: D

Cuestiones relacionadas