Estoy desarrollando una aplicación con ICS y fragmentos de Android 4.0.
Considere este ejemplo modificado de la aplicación de ejemplo de demostración de la API ICS 4.0.3 (nivel de API 15):
public class FragmentTabs extends Activity {
private static final String TAG = FragmentTabs.class.getSimpleName();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final ActionBar bar = getActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.setDisplayOptions(0, ActionBar.DISPLAY_SHOW_TITLE);
bar.addTab(bar.newTab()
.setText("Simple")
.setTabListener(new TabListener<SimpleFragment>(
this, "mysimple", SimpleFragment.class)));
if (savedInstanceState != null) {
bar.setSelectedNavigationItem(savedInstanceState.getInt("tab", 0));
Log.d(TAG, "FragmentTabs.onCreate tab: " + savedInstanceState.getInt("tab"));
Log.d(TAG, "FragmentTabs.onCreate number: " + savedInstanceState.getInt("number"));
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("tab", getActionBar().getSelectedNavigationIndex());
}
public static class TabListener<T extends Fragment> implements ActionBar.TabListener {
private final Activity mActivity;
private final String mTag;
private final Class<T> mClass;
private final Bundle mArgs;
private Fragment mFragment;
public TabListener(Activity activity, String tag, Class<T> clz) {
this(activity, tag, clz, null);
}
public TabListener(Activity activity, String tag, Class<T> clz, Bundle args) {
mActivity = activity;
mTag = tag;
mClass = clz;
mArgs = args;
// Check to see if we already have a fragment for this tab, probably
// from a previously saved state. If so, deactivate it, because our
// initial state is that a tab isn't shown.
mFragment = mActivity.getFragmentManager().findFragmentByTag(mTag);
if (mFragment != null && !mFragment.isDetached()) {
Log.d(TAG, "constructor: detaching fragment " + mTag);
FragmentTransaction ft = mActivity.getFragmentManager().beginTransaction();
ft.detach(mFragment);
ft.commit();
}
}
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (mFragment == null) {
mFragment = Fragment.instantiate(mActivity, mClass.getName(), mArgs);
Log.d(TAG, "onTabSelected adding fragment " + mTag);
ft.add(android.R.id.content, mFragment, mTag);
} else {
Log.d(TAG, "onTabSelected attaching fragment " + mTag);
ft.attach(mFragment);
}
}
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
if (mFragment != null) {
Log.d(TAG, "onTabUnselected detaching fragment " + mTag);
ft.detach(mFragment);
}
}
public void onTabReselected(Tab tab, FragmentTransaction ft) {
Toast.makeText(mActivity, "Reselected!", Toast.LENGTH_SHORT).show();
}
}
public static class SimpleFragment extends Fragment {
TextView textView;
int mNum;
/**
* When creating, retrieve this instance's number from its arguments.
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(FragmentTabs.TAG, "onCreate " + (savedInstanceState != null ? ("state " + savedInstanceState.getInt("number")) : "no state"));
if(savedInstanceState != null) {
mNum = savedInstanceState.getInt("number");
} else {
mNum = 25;
}
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.d(TAG, "onActivityCreated");
if(savedInstanceState != null) {
Log.d(TAG, "saved variable number: " + savedInstanceState.getInt("number"));
}
super.onActivityCreated(savedInstanceState);
}
@Override
public void onSaveInstanceState(Bundle outState) {
Log.d(TAG, "onSaveInstanceState saving: " + mNum);
outState.putInt("number", mNum);
super.onSaveInstanceState(outState);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(FragmentTabs.TAG, "onCreateView " + (savedInstanceState != null ? ("state: " + savedInstanceState.getInt("number")) : "no state"));
textView = new TextView(getActivity());
textView.setText("Hello world: " + mNum);
textView.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
return textView;
}
}
}
Aquí está la salida recuperada de ejecutar este ejemplo y luego girar el teléfono:
06-11 11:31:42.559: D/FragmentTabs(10726): onTabSelected adding fragment mysimple
06-11 11:31:42.559: D/FragmentTabs(10726): onCreate no state
06-11 11:31:42.559: D/FragmentTabs(10726): onCreateView no state
06-11 11:31:42.567: D/FragmentTabs(10726): onActivityCreated
06-11 11:31:45.286: D/FragmentTabs(10726): onSaveInstanceState saving: 25
06-11 11:31:45.325: D/FragmentTabs(10726): onCreate state 25
06-11 11:31:45.340: D/FragmentTabs(10726): constructor: detaching fragment mysimple
06-11 11:31:45.340: D/FragmentTabs(10726): onTabSelected attaching fragment mysimple
06-11 11:31:45.348: D/FragmentTabs(10726): FragmentTabs.onCreate tab: 0
06-11 11:31:45.348: D/FragmentTabs(10726): FragmentTabs.onCreate number: 0
06-11 11:31:45.348: D/FragmentTabs(10726): onCreateView state: 25
06-11 11:31:45.348: D/FragmentTabs(10726): onActivityCreated
06-11 11:31:45.348: D/FragmentTabs(10726): saved variable number: 25
06-11 11:31:45.348: D/FragmentTabs(10726): onCreateView no state
06-11 11:31:45.348: D/FragmentTabs(10726): onActivityCreated
Mi pregunta es, ¿por qué onCreateView y onActivityCreated se llaman dos veces? ¿La primera vez con un Bundle con el estado guardado y la segunda vez con un SavedInstanceState nulo?
Esto está causando problemas para retener el estado del fragmento en rotación.
Respuestas:
También me estuve rascando la cabeza por esto por un tiempo, y dado que la explicación de Dave es un poco difícil de entender, publicaré mi código (aparentemente funcionando):
Como puede ver, es muy parecido a la muestra de Android, aparte de no separarse en el constructor y usar reemplazar en lugar de agregar .
Después de mucho rasguño y prueba y error, descubrí que encontrar el fragmento en el constructor parece hacer que el problema doble de onCreateView desaparezca mágicamente (supongo que termina siendo nulo para onTabSelected cuando se llama a través de la ruta ActionBar.setSelectedNavigationItem () cuando estado de ahorro / restauración).
fuente
add
lugar dereplace
y gira la pantalla, tendrá muchos fragmentos 'onCreateView()
.Ok, esto es lo que descubrí.
Lo que no entendí es que todos los fragmentos que se adjuntan a una actividad cuando ocurre un cambio de configuración (el teléfono gira) se recrean y se vuelven a agregar a la actividad. (que tiene sentido)
Lo que sucedía en el constructor TabListener era que la pestaña se separaba si se encontraba y se adjuntaba a la actividad. Vea abajo:
Más adelante, en la actividad de Crear, se seleccionó la pestaña previamente seleccionada del estado de la instancia guardada. Vea abajo:
Cuando se selecciona la pestaña, se vuelve a adjuntar en la devolución de llamada onTabSelected.
El fragmento que se adjunta es la segunda llamada a los métodos onCreateView y onActivityCreated. (El primero es cuando el sistema está recreando la actividad y todos los fragmentos adjuntos) La primera vez que el paquete onSavedInstanceState habría guardado datos, pero no la segunda vez.
La solución es no separar el fragmento en el constructor TabListener, simplemente dejarlo adjunto. (Aún necesita encontrarlo en FragmentManager por su etiqueta) Además, en el método onTabSelected verifico si el fragmento está separado antes de adjuntarlo. Algo como esto:
fuente
He tenido el mismo problema con una actividad simple que lleva solo un fragmento (que a veces se reemplaza). Luego me di cuenta de que uso onSaveInstanceState solo en el fragmento (y onCreateView para verificar si hay SavedInstanceState), no en la actividad.
Al encender el dispositivo, la actividad que contiene los fragmentos se reinicia y se llama a onCreated. Allí adjunté el fragmento requerido (que es correcto en el primer inicio).
En el dispositivo, gire Android primero recreó el fragmento que era visible y luego llamó a onCreate de la actividad contenedora donde se adjuntó mi fragmento, reemplazando así el visible original.
Para evitar eso, simplemente cambié mi actividad para verificar si había guardadoInstanceState:
Ni siquiera sobrescribí onSaveInstanceState de la actividad.
fuente
Las dos respuestas votadas aquí muestran soluciones para una actividad con modo de navegación
NAVIGATION_MODE_TABS
, pero tuve el mismo problema con unNAVIGATION_MODE_LIST
. Hizo que mis Fragmentos perdieran inexplicablemente su estado cuando la orientación de la pantalla cambió, lo cual fue realmente molesto. Afortunadamente, gracias a su código útil, logré descifrarlo.Básicamente, cuando se usa una navegación de lista, `` onNavigationItemSelected ()
is automatically called when your activity is created/re-created, whether you like it or not. To prevent your Fragment's
onCreateView ()from being called twice, this initial automatic call to
onNavigationItemSelected ()should check whether the Fragment is already in existence inside your Activity. If it is, return immediately, because there is nothing to do; if it isn't, then simply construct the Fragment and add it to the Activity like you normally would. Performing this check prevents your Fragment from needlessly being created again, which is what causes
onCreateView () `para ser llamado dos veces!Vea mi
onNavigationItemSelected()
implementación a continuación.Tomé prestada inspiración para esta solución de aquí .
fuente
Me parece que se debe a que está creando una instancia de su TabListener cada vez ... por lo que el sistema está recreando su fragmento de SavedInstanceState y luego lo está haciendo nuevamente en su onCreate.
Debe envolver eso en un
if(savedInstanceState == null)
modo que solo se active si no hay SavedInstanceState.fuente