Fragmento de Android: cómo guardar estados de vistas en un fragmento cuando se empuja otro fragmento sobre él

En Android, un fragmento (digamos, FragA ) se agrega a la backstack y otro fragmento (por ejemplo, FragB ) llega a la cima. Ahora al golpear atrás, FragA llega a la cima y se llama a onCreateView() . Ahora tenía a FragA en un estado particular antes de que FragB fuera empujado a la cima.

Mi pregunta es ¿cómo puedo restaurar FragA a su estado anterior? ¿Hay alguna manera de guardar el estado (como decir en un paquete) y, de ser así, qué método debo anular?

En la guía de fragmentos del ejemplo de FragmentList puedes encontrar:

 @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("curChoice", mCurCheckPosition); } 

Que puedes usar más tarde de esta manera:

 @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (savedInstanceState != null) { // Restore last state for checked position. mCurCheckPosition = savedInstanceState.getInt("curChoice", 0); } } 

Soy un principiante en Fragments pero parece ser la solución de tu problema;) OnActivityCreated se invoca después de que el fragmento vuelve de la stack posterior.

Fragment’s onSaveInstanceState(Bundle outState) nunca se onSaveInstanceState(Bundle outState) menos que la actividad del fragmento lo llame sobre sí mismo y los fragmentos adjuntos. Por lo tanto, este método no se SaveInstanceState hasta que algo (por lo general, rotación) fuerce la actividad a SaveInstanceState y la restaure más tarde. Pero si solo tiene una actividad y un gran conjunto de fragmentos dentro (con el uso intensivo de replace ) y las ejecuciones de aplicaciones solo en una actividad de orientación, onSaveInstanceState(Bundle outState) puede que no se onSaveInstanceState(Bundle outState) durante un tiempo prolongado.

Sé tres posibles soluciones.

El primero:

use los argumentos del fragmento para contener datos importantes:

 public class FragmentA extends Fragment { private static final String PERSISTENT_VARIABLE_BUNDLE_KEY = "persistentVariable"; private EditText persistentVariableEdit; public FragmentA() { setArguments(new Bundle()); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_a, null); persistentVariableEdit = (EditText) view.findViewById(R.id.editText); TextView proofTextView = (TextView) view.findViewById(R.id.textView); Bundle mySavedInstanceState = getArguments(); String persistentVariable = mySavedInstanceState.getString(PERSISTENT_VARIABLE_BUNDLE_KEY); proofTextView.setText(persistentVariable); view.findViewById(R.id.btnPushFragmentB).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getFragmentManager() .beginTransaction() .replace(R.id.frameLayout, new FragmentB()) .addToBackStack(null) .commit(); } }); return view; } @Override public void onPause() { super.onPause(); String persistentVariable = persistentVariableEdit.getText().toString(); getArguments().putString(PERSISTENT_VARIABLE_BUNDLE_KEY, persistentVariable); } } 

La segunda, pero menos pedante, retención de variables en singletons

El tercero – no replace() fragmentos, sino que los add() / show() / hide() su lugar.

Solo tenga en cuenta que si trabaja con Fragments usando ViewPager, es bastante fácil. Solo necesita llamar a este método: setOffscreenPageLimit() .

Accordign a los documentos:

Establezca el número de páginas que deben conservarse en cualquier lado de la página actual en la jerarquía de vistas en un estado inactivo. Las páginas más allá de este límite se recrearán desde el adaptador cuando sea necesario.

Problema similar aquí

Simplemente infle su Vista por una vez.

Ejemplo siguiente:

 public class AFragment extends Fragment { private View mRootView; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if(mRootView==null){ mRootView = inflater.inflate(R.id.fragment_a, container, false); //...... } return mRootView; } 

}

Trabajé con un problema muy similar a esto. Como sabía que con frecuencia regresaría a un fragmento anterior, verifiqué si el fragmento .isAdded() era verdadero, y si es así, en lugar de hacer una transaction.replace() solo hago una transaction.show() . Esto evita que el fragmento se vuelva a crear si ya está en la stack; no es necesario guardar el estado.

 Fragment target = ; FragmentTransaction transaction = getFragmentManager().beginTransaction(); transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN); if(target.isAdded()) { transaction.show(target); } else { transaction.addToBackStack(button_id + "stack_item"); transaction.replace(R.id.page_fragment, target); } transaction.commit(); 

Otra cosa que hay que tener en cuenta es que, si bien esto conserva el orden natural de los fragmentos, es posible que aún necesites manejar la actividad en sí, destruida y recreada en el cambio de orientación (config). Para solucionar esto en AndroidManifest.xml para su nodo:

 android:configChanges="orientation|screenSize" 

En Android 3.0 y screenSize posteriores, el screenSize es aparentemente obligatorio.

Buena suerte

La mejor solución que encontré es la siguiente:

onSavedInstanceState (): siempre llamado dentro de fragmento cuando la actividad se va a cerrar (Mover actividad de uno a otro o cambios de configuración). Entonces, si estamos llamando a múltiples fragmentos en la misma actividad, entonces tenemos que usar el siguiente enfoque:

Use OnDestroyView () del fragmento y guarde todo el objeto dentro de ese método. Entonces OnActivityCreated (): compruebe que si el objeto es nulo o no (porque este método llama cada vez). Ahora restaura el estado de un objeto aquí.

¡Funciona siempre!

si está manejando los cambios de configuración en su actividad de fragmento especificada en el manifiesto de Android como este

  

entonces el onSaveInstanceState del fragmento no se invocará y el objeto savedInstanceState siempre será nulo.

Usé un enfoque híbrido para fragmentos que contienen una vista de lista. Parece ser un rendimiento ya que no reemplazo el fragmento actual sino que agrego el nuevo fragmento y escondo el actual. Tengo el siguiente método en la actividad que aloja mis fragmentos:

 public void addFragment(Fragment currentFragment, Fragment targetFragment, String tag) { FragmentManager fragmentManager = getSupportFragmentManager(); FragmentTransaction transaction = fragmentManager.beginTransaction(); transaction.setCustomAnimations(0,0,0,0); transaction.hide(currentFragment); // use a fragment tag, so that later on we can find the currently displayed fragment transaction.add(R.id.frame_layout, targetFragment, tag) .addToBackStack(tag) .commit(); } 

Utilizo este método en mi fragmento (que contiene la vista de lista) cada vez que se hace clic o toca un elemento de la lista (y, por lo tanto, debo iniciar / mostrar el fragmento de detalles):

 FragmentManager fragmentManager = getActivity().getSupportFragmentManager(); SearchFragment currentFragment = (SearchFragment) fragmentManager.findFragmentByTag(getFragmentTags()[0]); DetailsFragment detailsFragment = DetailsFragment.newInstance("some object containing some details"); ((MainActivity) getActivity()).addFragment(currentFragment, detailsFragment, "Details"); 

getFragmentTags() devuelve una matriz de cadenas que utilizo como tags para diferentes fragmentos cuando agrego un nuevo fragmento (consulte el método transaction.add en el método addFragment anterior).

En el fragmento que contiene la vista de lista, hago esto en su método onPause ():

 @Override public void onPause() { // keep the list view's state in memory ("save" it) // before adding a new fragment or replacing current fragment with a new one ListView lv = (ListView) getActivity().findViewById(R.id.listView); mListViewState = lv.onSaveInstanceState(); super.onPause(); } 

Luego, en onCreateView del fragmento (en realidad en un método que se invoca en onCreateView), restauro el estado:

 // Restore previous state (including selected item index and scroll position) if(mListViewState != null) { Log.d(TAG, "Restoring the listview's state."); lv.onRestoreInstanceState(mListViewState); } 

No creo que onSaveInstanceState sea ​​una buena solución. solo usa para la actividad que ha sido eliminada.

Desde Android 3.0 el Fragmen ha sido administrador por FragmentManager, la condición es: una actividad de mapeo de manny fragmentos, cuando el fragmento se agrega (no se reemplaza: se volverá a crear) en BackStack, la vista se desaprovechará. cuando regrese al último, se mostrará como antes.

Así que creo que fragmentManger y la transacción es lo suficientemente bueno para manejarlo.

Al final, después de probar muchas de estas complicadas soluciones, ya que solo necesitaba guardar / restaurar un solo valor en mi Fragment (el contenido de un EditText), y aunque podría no ser la solución más elegante, crear una SharedPreference y almacenar mi estado ahí funcionó para mí

 private ViewPager viewPager; viewPager = (ViewPager) findViewById(R.id.pager); mAdapter = new TabsPagerAdapter(getSupportFragmentManager()); viewPager.setAdapter(mAdapter); viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageSelected(int position) { // on changing the page // make respected tab selected actionBar.setSelectedNavigationItem(position); } @Override public void onPageScrolled(int arg0, float arg1, int arg2) { } @Override public void onPageScrollStateChanged(int arg0) { } }); } @Override public void onTabReselected(Tab tab, FragmentTransaction ft) { } @Override public void onTabSelected(Tab tab, FragmentTransaction ft) { // on tab selected // show respected fragment view viewPager.setCurrentItem(tab.getPosition()); } @Override public void onTabUnselected(Tab tab, FragmentTransaction ft) { }