19

Estoy desarrollando una aplicación para Android dirigida a dispositivos 2.x y 3.0 y, por lo tanto, estoy usando la API compatibilty. Estoy probando en Android 2.0.Reemplazo de fragmentos y cambio de orientación

Estoy tratando de reemplazar un fragmento mostrado por otro (formulario de búsqueda con resultados de búsqueda) y estoy experimentando un bloqueo en el cambio de orientación cuando se muestra el segundo fragmento (resultados).

Básicamente, tienen una actividad, que incluye un fragmento definido en xml diseño como

<fragment class="org.prevoz.android.search.SearchFormFragment" 
       android:id = "@+id/search_form_fragment" 
       android:layout_width = "fill_parent" 
       android:layout_height = "fill_parent" /> 

El primer fragmento (SearchFormFragment) se muestra como un defecto. Cuando el usuario toca un botón de "búsqueda", se sustituye la SearchFormFragment con SearchResultsFragment, que dirige una AsyncTask (por eso quiero retenerla) con

// Show the search results fragment 
SearchResultsFragment newSearch = new SearchResultsFragment(from, to, when); 
newSearch.setRetainInstance(true); 

FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out); 
transaction.replace(R.id.search_form_fragment, newSearch);  
transaction.addToBackStack(null); 
transaction.commit(); 

Sin embargo, cuando se visualiza el SearchFormFragment y se cambia la orientación, mi aplicación se bloquea con

ERROR/AndroidRuntime(334): FATAL EXCEPTION: main 
ERROR/AndroidRuntime(334): java.lang.RuntimeException: Unable to start activity ComponentInfo{org.prevoz.android/org.prevoz.android.search.MainActivity}: android.view.InflateException: Binary XML file line #13: Error inflating class fragment 
ERROR/AndroidRuntime(334):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2663) 
ERROR/AndroidRuntime(334):  at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2679) 
ERROR/AndroidRuntime(334):  at android.app.ActivityThread.handleRelaunchActivity(ActivityThread.java:3815) 
ERROR/AndroidRuntime(334):  at android.app.ActivityThread.access$2400(ActivityThread.java:125) 
ERROR/AndroidRuntime(334):  at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2037) 
ERROR/AndroidRuntime(334):  at android.os.Handler.dispatchMessage(Handler.java:99) 
ERROR/AndroidRuntime(334):  at android.os.Looper.loop(Looper.java:123) 
ERROR/AndroidRuntime(334):  at android.app.ActivityThread.main(ActivityThread.java:4627) 
ERROR/AndroidRuntime(334):  at java.lang.reflect.Method.invokeNative(Native Method) 
ERROR/AndroidRuntime(334):  at java.lang.reflect.Method.invoke(Method.java:521) 
ERROR/AndroidRuntime(334):  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:868) 
ERROR/AndroidRuntime(334):  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:626) 
ERROR/AndroidRuntime(334):  at dalvik.system.NativeStart.main(Native Method) 
ERROR/AndroidRuntime(334): Caused by: android.view.InflateException: Binary XML file line #13: Error inflating class fragment 
ERROR/AndroidRuntime(334):  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:582) 
ERROR/AndroidRuntime(334):  at android.view.LayoutInflater.rInflate(LayoutInflater.java:618) 
ERROR/AndroidRuntime(334):  at android.view.LayoutInflater.inflate(LayoutInflater.java:407) 
ERROR/AndroidRuntime(334):  at android.view.LayoutInflater.inflate(LayoutInflater.java:320) 
ERROR/AndroidRuntime(334):  at android.view.LayoutInflater.inflate(LayoutInflater.java:276) 
ERROR/AndroidRuntime(334):  at com.android.internal.policy.impl.PhoneWindow.setContentView(PhoneWindow.java:198) 
ERROR/AndroidRuntime(334):  at android.app.Activity.setContentView(Activity.java:1647) 
ERROR/AndroidRuntime(334):  at org.prevoz.android.search.MainActivity.onCreate(MainActivity.java:40) 
ERROR/AndroidRuntime(334):  at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1047) 
ERROR/AndroidRuntime(334):  at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2627) 
ERROR/AndroidRuntime(334):  ... 12 more 
ERROR/AndroidRuntime(334): Caused by: java.lang.IllegalStateException: Fragment org.prevoz.android.search.SearchFormFragment did not create a view. 
ERROR/AndroidRuntime(334):  at android.support.v4.app.FragmentActivity.onCreateView(FragmentActivity.java:281) 
ERROR/AndroidRuntime(334):  at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:558) 
ERROR/AndroidRuntime(334):  ... 21 more 
WARN/ActivityManager(59): Force finishing activity org.prevoz.android/.search.MainActivity 

El código onCreateView relevante de SearchFormFragment se consiga llamar y me devuelven un punto de vista válido:

@Override 
public View onCreateView(LayoutInflater inflater, 
         ViewGroup container, 
         Bundle savedInstanceState) 
{  
    View newView = inflater.inflate(R.layout.search_form_frag, container, false); 
    return newView; 
} 

El bloqueo solo ocurre si se muestra SearchResultsFragment; el cambio funciona bien si se muestra elFormularioFormulario predeterminado.

Entonces, ¿cómo retener el segundo estado de fragmento a través del cambio de orientación? La documentación sobre API y cambios de estado es realmente deficiente.

Respuesta

28

No cree el SearchFormFragment en XML. En su lugar, tenga un FrameLayout vacío que rellene en Activity.onCreate() sin agregarlo a la pila posterior. De esta forma, el Activity mantendrá el actual Fragment en lugar de intentar agregar el especificado en XML.

Además, el uso AsyncLoader puede haber un mejor enfoque, ver http://code.google.com/p/android/issues/detail?id=14944

+0

He utilizado cargadores para resolver mi problema, lo que funcionó. Gracias. – Mavrik

+0

has hecho mi día :) – Oli

4

Después de luchar con esto durante muchas horas, por fin tengo. El problema no está en la configuración o en la forma en que llamamos a la clase Fragment. Tiene que ver con un mensaje incorrecto que se muestra en la pantalla. Si tiene un cheque para un contenedor que es nulo en su onCreateView() en su clase Ftagment, obtendrá ese mensaje de "no se puede inflar el fragmento", en lugar de contenedor es un mensaje nulo. Por lo tanto, no verifique el contenedor nulo en su onCreateView(). Así que asegúrese de que su onCreateView() se parece a esto:

@Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     return(inflater.inflate(R.layout.title_layout, container, false)); 
    } 

y no como este:

@Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     if(container == null){ 
      return null;         
     } 
     return(inflater.inflate(R.layout.title_layout, container, false)); 
    } 
+0

No deberías usar 'Inflator.inflate' con el contenedor provisto en' onCreateView() ', el elemento principal se agrega automágicamente después de completar' onCreateView() '. En su lugar, debe inflar pasar 'null' como parámetro del contenedor. – Graeme

+0

Eso es contradictorio. ¿Cuál es el punto de 'contenedor', entonces? – Cheezmeister

+0

@Graeme el último parámetro de 'false' significa que no se adjunta la vista inflada al contenedor, y el contenedor se pasa de modo que se use el' LayoutParam's correcto para la vista inflada. –

4

Por razones de referencia: Hemos sido capaces de resolver un problema muy similar añadiendo una android:id en nuestro fragmento creado en XML.

Este requisito muy importante para apoyar la libre rotación de una colisión sólo es mentioned as a side-note in the documentation:

Nota: Cada fragmento requiere un identificador único que el sistema puede uso para restaurar el fragmento si se reinicia la actividad (y que puede utilizar para capturar el fragmento para realizar transacciones, como eliminarlo).

+0

Esta es la única respuesta correcta. – mkuech

Cuestiones relacionadas