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:
- ¿Puedo eliminar un fragmento definido en un archivo layout.xml?
- Android - necesito algunas aclaraciones de fragmentos vs actividades y vistas
- RecyclerView no se desplaza hacia abajo
- Usar getSupportFragmentManager () Dentro de PreferenceActivity
- Botón de acción flotante que no se muestra completamente dentro de un fragmento
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?
- Fragmento de Android encreateview llamado en el cambio de ficha
- Android onClick en XML vs. OnClickListener
- ¿Cómo agrego un Fragmento de Android a mi FragmentActivity?
- Android TabsAdapter con ActionbarSherlock
- getActivity (). findViewById (R.layout.contacts_list_view) devuelve null
- OnKeyDown dentro del fragmento WebView
- Manera de la insensatez a manejar Fragmento en cambio de la orientación
- Cómo manejar onClick en Fragments
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?