¿Por qué no Fragment retener el estado cuando se gira la pantalla?

He tenido problemas para obtener algunas subclases de DialogPreference personalizadas dentro de un PreferenceFragment para permanecer visibles cuando se gira la pantalla. No experimento este problema al usar una PreferenceActivity, por lo que no sé si se trata de un error de Android o un problema con mi código, pero me gustaría que alguien confirme si están teniendo la misma experiencia.

Para probar esto, primero cree una pantalla de preferencias que contenga al menos un DialogPreference (no importa qué subclase). Luego muéstrelo en una PreferenciaActividad. Cuando ejecute su aplicación, presione en DialogPreference para que aparezca el cuadro de diálogo. A continuación, gire la pantalla para cambiar la orientación. ¿Permanece visible el cuadro de diálogo?

A continuación, intente lo mismo, pero con un PreferenceFragment para mostrar sus preferencias en lugar de una PreferenceActivity. De nuevo, ¿el cuadro de diálogo permanece visible cuando gira la pantalla?

Hasta ahora, he encontrado que el diálogo permanecerá visible si se utiliza una PreferenceActivity, pero no si se utiliza un PreferenceFragment. Observando el código fuente de DialogPreference , parece que el comportamiento correcto es que el cuadro de diálogo permanezca visible, ya que isDialogShowing es la información de estado que se guarda cuando onSaveInstanceState() se llama en la reorientación de la pantalla. Por lo tanto, creo que un error puede impedir que el PreferenceFragment (y todo lo que hay dentro de él) restaurar esa información de estado.

Si se trata de un error de Android, tiene implicaciones de gran alcance, ya que cualquiera que utilice PreferenceFragment no puede guardar y restaurar la información de estado.

¿Puede alguien confirmar? Si no es un error, ¿qué está pasando?

Finalmente descubierto una solución a este problema. Resulta que no es un error, sino un problema / supervisión en la documentación para desarrolladores de Android.

Usted ve, yo estaba siguiendo el tutorial PreferenceFragment aquí . Ese artículo le dice que haga lo siguiente para instanciar su PreferenceFragment dentro de una actividad:

 public class SettingsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } } 

El problema con esto es que cuando se cambia la orientación de la pantalla (o cualquier otra acción que destruye y vuelve a crear la actividad), su PreferenceFragment se creará dos veces , que es lo que hace que pierda su estado.

La primera creación se realizará a través de la llamada de la actividad a super.onCreate() (que se muestra arriba), que llamará el método onActivityCreated() para su PreferenceFragment () y el método onRestoreInstanceState() para cada preferencia que contenga. Éstos restaurarán con éxito el estado de todo.

Pero una vez que esa llamada a super.onCreate() devuelve, puede ver que el método onCreate() entonces continuará para crear el PreferenceFragment una segunda vez. Debido a que es inútilmente creado de nuevo (y esta vez, sin información de estado!), Todo el estado que fue restaurado con éxito será completamente descartado / perdido. Esto explica por qué un DialogPreference que puede estar mostrando en el momento en que se destruye la actividad ya no será visible una vez que la actividad se vuelve a crear.

¿Cuál es la solución? Bueno, solo agrega un pequeño cheque para determinar si el PreferenceFragment ya se ha creado, así:

 public class SettingsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Fragment existingFragment = getFragmentManager().findFragmentById(android.R.id.content); if (existingFragment == null || !existingFragment.getClass().equals(SettingsFragment.class)) { // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } } } 

O de otra manera es simplemente comprobar si onCreate() está destinado a restaurar el estado o no, así:

 public class SettingsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } } } 

Así que supongo que la lección aprendida aquí es que onCreate() tiene un doble rol: puede configurar una Actividad por primera vez, o puede restaurar desde un estado anterior.

La respuesta aquí me llevó a darme cuenta de esta solución.

Yo mismo he tenido este problema. Hay un error donde el DialogFragment no restaura el estado porque es nulo, o al menos me pasó a mí.

El uso de múltiples fuentes que finalmente tiene una solución de trabajo. Haga que su diálogo extienda este BaseDialogFragment :

 import android.app.Dialog; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.support.v4.app.DialogFragment; import com.actionbarsherlock.app.SherlockDialogFragment; public class BaseDialogFragment extends DialogFragment { @Override public void onCreate(Bundle savedInstanceState) { if (savedInstanceState == null || savedInstanceState.isEmpty()) savedInstanceState = WorkaroundSavedState.savedInstanceState; setRetainInstance(true); Log.d("TAG", "saved instance state oncreate: " + WorkaroundSavedState.savedInstanceState); super.onCreate(savedInstanceState); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { if (savedInstanceState == null || savedInstanceState.isEmpty()) savedInstanceState = WorkaroundSavedState.savedInstanceState; Log.d("TAG", "saved instance state oncretaedialog: " + WorkaroundSavedState.savedInstanceState); return super.onCreateDialog(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (savedInstanceState == null || savedInstanceState.isEmpty()) savedInstanceState = WorkaroundSavedState.savedInstanceState; Log.d("TAG", "saved instance state oncretaeview: " + WorkaroundSavedState.savedInstanceState); return super.onCreateView(inflater, container, savedInstanceState); } @Override public void onDestroyView() // necessary for restoring the dialog { if (getDialog() != null && getRetainInstance()) getDialog().setOnDismissListener(null); super.onDestroyView(); } @Override public void onSaveInstanceState(Bundle outState) { // ... super.onSaveInstanceState(outState); WorkaroundSavedState.savedInstanceState = outState; Log.d("TAG", "saved instance state onsaveins: " + WorkaroundSavedState.savedInstanceState); } @Override public void onDestroy() { WorkaroundSavedState.savedInstanceState = null; super.onDestroy(); } /** * Static class that stores the state of the task across orientation * changes. There is a bug in the compatibility library, at least as of the * 4th revision, that causes the save state to be null in the dialog's * onRestoreInstanceState. */ public static final class WorkaroundSavedState { public static Bundle savedInstanceState; } } 

Tenga en cuenta que en cualquier subclases cuyos métodos tienen un parámetro savedInstanceState , es posible que tenga que llamar a super con WorkaroundSavedState.savedInstanceState . Y cuando se está restaurando el estado (es decir, en onCreate() , simplemente ignore el savedInstanceState y en su lugar use WorkaroundSavedState.savedInstanceState soporte estático no es la solución más limpia, pero funciona.Sólo asegúrese de establecerlo en null en su onDestroy() .

En cualquier caso, mi DialogFragment no desaparece cuando hago girar la pantalla (y eso es sin ningún configChanges ). Déjeme saber si este código se ocupa de su problema y si no voy a echar un vistazo a lo que está pasando. También tenga en cuenta que no he probado esto dentro de PreferenceFragment pero en su lugar otro Fragment de la clase de compatibilidad o de ActionBarSherlock .

  • Androide. Color CheckBoxPreference
  • Valor predeterminado del tono de llamada de preferencia
  • Instanciar una clase interna (Preferencia) en un archivo xml
  • ScrollView> LinearLayout> PreferenceFragment es altura cero
  • DialogFragment en PreferenceActivity
  • PreferenceActivity funciona correctamente en Android 2.1, pero no en 4.1 (acolchado)
  • Iniciar actividad de los encabezados de preferencia
  • Cambiar el fondo PreferenceActivity
  • Nuevo tema incorrecto de la biblioteca de soporte de preferencias en tiempo de ejecución
  • Actividad de Android - DataBinding con preferencias
  • Cómo ocultar el divisor entre las preferencias en el fragmento
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.