android-3.0-honeycomb - jelly - supported api 3 que significa
IllegalArgumentException: no se ha encontrado ninguna vista para el id for fragment cuando se cambian rápidamente las pestañas de ActionBar (4)
Estoy desarrollando una aplicación para Android para tabletas y no estoy usando la biblioteca de compatibilidad.
Solo hay una Actividad y usa la Barra de acciones con 3 pestañas. En el TabListener uso setContentView para cargar el diseño específico de esa pestaña, y luego agrego los fragmentos relevantes a sus marcos. Esto casi funciona exactamente como yo quiero, excepto cuando cambias entre las pestañas lo suficientemente rápido como para que la aplicación se cuelgue.
Estoy usando una Samsung Galaxy Tab ya que mi dispositivo de depuración y las pestañas de conmutación son realmente rápidas. A un ritmo normal, puedo tocar adelante y atrás entre ellos y las páginas se cargan al instante. El problema es cuando cambio entre las pestañas.
Al principio obtuve un
IllegalStateException: Fragment not added
como se ve aquí: http://code.google.com/p/android/issues/detail?id=17029 Siguiendo la sugerencia de usar los bloques try / catch enTabUnselected, hice la aplicación un poco más robusta, pero eso llevó a el problema en cuestión:
IllegalArgumentException: No view found for id 0x... for fragment ...
No he encontrado ningún otro caso en la web de alguien que tenga el mismo problema, por lo que me preocupa que pueda estar haciendo algo que no es compatible. Nuevamente , lo que trato de hacer es usar 3 diseños diferentes en una actividad: cuando haces clic en una pestaña, el oyente llama a setContentView para cambiar el diseño y luego agrega los fragmentos. Funciona a la perfección a menos que empiece a cambiar de manera agresiva entre pestañas.
Tengo la idea de esto en: http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/app/FragmentTabs.html y en lugar de que el TabListener mantenga una referencia a un fragmento , Tengo una variedad de ellos. Además, no estoy usando attach / detach ya que esos solo se agregaron en API 13.
Mi teoría es que setContentView no ha terminado de crear las vistas, y es por eso que FragmentTransaction no puede agregarlas, O los fragmentos se agregan a una pestaña cuando se selecciona otra y se llama a setContentView, destruyendo el otro conjunto de vistas.
Traté de hackear algo para ralentizar el cambio de pestañas, pero no llegué a ningún lado.
Aquí está el código para mi TabListener:
private class BTabListener<T extends Fragment> implements ActionBar.TabListener{
private int mLayout;
private Fragment[] mFrags;
private TabData mTabData;
private Activity mAct;
private boolean mNoNewFrags;
public BTabListener(Activity act, int layout, TabData td, boolean frags){
mLayout = layout;
mTabData = td;
mAct = act;
mNoNewFrags = frags;
mFrags = new Fragment[mTabData.fragTags.length];
for(int i=0; i<mFrags.length; i++){
//on an orientation change, this will find the fragments that were recreated by the system
mFrags[i] = mAct.getFragmentManager().findFragmentByTag(mTabData.fragTags[i]);
}
}
@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
//this gets called _after_ unselected
//note: unselected wont have been called after an orientation change!
//we also need to watch out because tab 0 always gets selected when adding the tabs
//set the view for this tab
mAct.setContentView(mLayout);
for(int i=0; i<mFrags.length; i++){
//this will be null when the tab is first selected
if(mFrags[i]==null ){
mFrags[i] = Fragment.instantiate(GUITablet.this, mTabData.classes[i].getName());
}
//if there was an orientation change when we were on this page, the fragment is already added
if(!mNoNewFrags || mDefaultTab!=tab.getPosition()){
ft.add(mTabData.containterIDs[i], mFrags[i], mTabData.fragTags[i]);
}
}
mNoNewFrags = false;
}
@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
// this gets called when another tab is selected, before it''s onSelected method
for(Fragment f : mFrags){
try{ //extra safety measure
ft.remove(f);
}catch(Exception e){
e.printStackTrace();
System.out.println("unselect couldnt remove");
}
}
}
}
Y finalmente, el rastro de la pila:
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): java.lang.IllegalArgumentException: No view found for id 0x7f0b0078 for fragment Fraggle{40ab2230 #2 id=0x7f0b0078 dummy2}
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:729)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.moveToState(FragmentManager.java:926)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.BackStackRecord.run(BackStackRecord.java:578)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl.execPendingActions(FragmentManager.java:1226)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.FragmentManagerImpl$1.run(FragmentManager.java:374)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Handler.handleCallback(Handler.java:587)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Handler.dispatchMessage(Handler.java:92)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.os.Looper.loop(Looper.java:132)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at android.app.ActivityThread.main(ActivityThread.java:4028)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at java.lang.reflect.Method.invokeNative(Native Method)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at java.lang.reflect.Method.invoke(Method.java:491)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:844)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:602)
09-29 01:53:08.200: ERROR/AndroidRuntime(4611): at dalvik.system.NativeStart.main(Native Method)
¡¡GRACIAS!!
Bien, encontré una forma de evitar esto:
Coloque las referencias a los fragmentos en los archivos de diseño y rodee la llamada setContentView en onTabSelected en un bloque try / catch.
¡El manejo de excepciones se encargó de eso!
Ver un caso similar en un informe de bloqueo de Google Play.
java.lang.IllegalStateException:
Fragment not added: MessageDisplayFragment{406e28f0 #2 id=0x7f0b0020}
at android.app.BackStackRecord.hide(BackStackRecord.java:397)
at org.kman.AquaMail.ui.AccountListActivity$UIMediator_API11_TwoPane.
closeMessageDisplay(AccountListActivity.java:2585)
Código relevante:
AbsMessageFragment fragDisplay = (AbsMessageFragment)
mFM.findFragmentById(R.id.fragment_id_message_display);
if (fragDisplay != null) {
FragmentTransaction ft = mFM.beginTransaction();
ft.setTransition(FragmentTransaction.TRANSIT_NONE);
ft.show(some other fragment);
ft.hide(fragDisplay);
^^ Aquí es donde se bloquea
El fragmento definitivamente está allí (fue devuelto por findFragmentById), pero llamar a hide () explotó con la "IllegalStateException: Fragment not added"
¿No añadido? ¿Cómo no se agrega si findFragmentById fue capaz de encontrarlo?
Todas las llamadas de FragmentManager se realizan a partir del subproceso de UI.
El fragmento que se eliminó (fragDisplay) se agregó antes y estoy seguro de que se ha comprometido FragmentTransaction.
Se corrigió mediante un bucle en mis fragmentos, eliminando cualquiera de los que se han agregado y luego agregando el nuevo. También compruebe primero que no está tratando de reemplazarlo con nulo o un fragmento existente.
Editar: parece que fue solo
getChildFragmentManager().executePendingTransactions();
que lo detuvo de estrellarse
private void replaceCurrentTabFragment(TabFragment tabFragment){
if (tabFragment == null){ return; }
if (tabFragment == getActiveTabFragment()){ return; }
FragmentTransaction ft = getChildFragmentManager().beginTransaction();
for (TabFragment fragment : mTabButtons.values()){
if (((Fragment)fragment).isAdded()){
ft.remove((Fragment)fragment);
}
}
ft.add(R.id.fragment_frameLayout, (Fragment) tabFragment);
ft.commit();
getChildFragmentManager().executePendingTransactions();
}
private TabFragment getActiveTabFragment(){
return (TabFragment) getChildFragmentManager().findFragmentById(R.id.fragment_frameLayout);
}
Sé que es una pregunta un poco vieja, pero estaba teniendo lo mismo y encontrar un trabajo diferente.
El método en sí se describe aquí. Evite volver a crear la misma vista cuando realice el cambio de pestaña.
pero en el contexto de este bloqueo específico, hacer Mostrar / Ocultar en lugar de agregar / reemplazar evita múltiples llamadas rápidas a onCreateView en el fragmento.
Mi código final terminó algo como esto:
@Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
if (fragments[tab.getPosition()] == null) {
switch(tag.getPosition()){
case 0: fragments[tab.getPosition()] = new // fragment for this position
break;
// repeat for all the tabs, for each `case`
}
fragmentTransaction.add(R.id.container, fragments[tab.getPosition()]);
}else{
fragmentTransaction.show(fragments[tab.getPosition()]);
}
}
@Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction fragmentTransaction) {
if (fragments[tab.getPosition()] != null)
fragmentTransaction.hide(fragments[tab.getPosition()]);
}