fragments - tabbed activity android studio
ViewPager y fragmentos: ¿cuál es la forma correcta de almacenar el estado del fragmento? (10)
¿Qué es ese BasePagerAdapter
? Debe usar uno de los adaptadores de buscapersonas estándar, ya sea FragmentPagerAdapter
o FragmentStatePagerAdapter
, dependiendo de si desea que los fragmentos que ya no son necesarios para ViewPager
se conserven (el primero) o se guarden su estado (el último) -Creado si es necesario de nuevo.
El código de ejemplo para usar ViewPager
se puede encontrar here
Es cierto que la administración de fragmentos en un localizador de vista en instancias de actividad es un poco complicada, porque el FragmentManager
en el marco se encarga de guardar el estado y restaurar cualquier fragmento activo que haya realizado el localizador. Todo lo que realmente significa es que el adaptador al inicializarse debe asegurarse de que se vuelva a conectar con cualquier fragmento restaurado que haya. Puede ver el código de FragmentPagerAdapter
o FragmentStatePagerAdapter
para ver cómo se hace esto.
Los fragmentos parecen ser muy agradables para la separación de la lógica UI en algunos módulos. Pero junto con ViewPager
su ciclo de vida todavía es ViewPager
para mí. ¡Así que los pensamientos del Gurú son muy necesarios!
Editar
Ver la solución tonta a continuación ;-)
Alcance
La actividad principal tiene un ViewPager
con fragmentos. Esos fragmentos podrían implementar una lógica un poco diferente para otras actividades (de dominio), por lo que los datos de los fragmentos se rellenan a través de una interfaz de devolución de llamada dentro de la actividad. Y todo funciona bien en el primer lanzamiento, pero! ...
Problema
Cuando la actividad se recrea (por ejemplo, en el cambio de orientación), también lo hacen los ViewPager
del ViewPager
. El código (que encontrará a continuación) dice que cada vez que se crea la actividad, trato de crear un nuevo adaptador de fragmentos de ViewPager
igual que los fragmentos (tal vez este sea el problema) pero FragmentManager ya tiene todos estos fragmentos almacenados en algún lugar (¿dónde?) y comienza el mecanismo de recreación para aquellos. Por lo tanto, el mecanismo de recreación llama el fragmento "antiguo" enAttach, onCreateView, etc. con mi llamada de interfaz de devolución de llamada para iniciar los datos a través del método implementado de la Actividad. Pero este método apunta al fragmento creado recientemente que se crea a través del método onCreate de la Actividad.
Problema
Tal vez estoy usando patrones incorrectos pero incluso el libro de Android 3 Pro no tiene mucho sobre eso. Entonces, por favor , dame uno o dos golpes y señala cómo hacerlo de la manera correcta. ¡Muchas gracias!
Código
Actividad principal
public class DashboardActivity extends BasePagerActivity implements OnMessageListActionListener {
private MessagesFragment mMessagesFragment;
@Override
protected void onCreate(Bundle savedInstanceState) {
Logger.d("Dash onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.viewpager_container);
new DefaultToolbar(this);
// create fragments to use
mMessagesFragment = new MessagesFragment();
mStreamsFragment = new StreamsFragment();
// set titles and fragments for view pager
Map<String, Fragment> screens = new LinkedHashMap<String, Fragment>();
screens.put(getApplicationContext().getString(R.string.dashboard_title_dumb), new DumbFragment());
screens.put(getApplicationContext().getString(R.string.dashboard_title_messages), mMessagesFragment);
// instantiate view pager via adapter
mPager = (ViewPager) findViewById(R.id.viewpager_pager);
mPagerAdapter = new BasePagerAdapter(screens, getSupportFragmentManager());
mPager.setAdapter(mPagerAdapter);
// set title indicator
TitlePageIndicator indicator = (TitlePageIndicator) findViewById(R.id.viewpager_titles);
indicator.setViewPager(mPager, 1);
}
/* set of fragments callback interface implementations */
@Override
public void onMessageInitialisation() {
Logger.d("Dash onMessageInitialisation");
if (mMessagesFragment != null)
mMessagesFragment.loadLastMessages();
}
@Override
public void onMessageSelected(Message selectedMessage) {
Intent intent = new Intent(this, StreamActivity.class);
intent.putExtra(Message.class.getName(), selectedMessage);
startActivity(intent);
}
BasePagerActivity aka helper
public class BasePagerActivity extends FragmentActivity {
BasePagerAdapter mPagerAdapter;
ViewPager mPager;
}
Adaptador
public class BasePagerAdapter extends FragmentPagerAdapter implements TitleProvider {
private Map<String, Fragment> mScreens;
public BasePagerAdapter(Map<String, Fragment> screenMap, FragmentManager fm) {
super(fm);
this.mScreens = screenMap;
}
@Override
public Fragment getItem(int position) {
return mScreens.values().toArray(new Fragment[mScreens.size()])[position];
}
@Override
public int getCount() {
return mScreens.size();
}
@Override
public String getTitle(int position) {
return mScreens.keySet().toArray(new String[mScreens.size()])[position];
}
// hack. we don''t want to destroy our fragments and re-initiate them after
@Override
public void destroyItem(View container, int position, Object object) {
// TODO Auto-generated method stub
}
}
Fragmento
public class MessagesFragment extends ListFragment {
private boolean mIsLastMessages;
private List<Message> mMessagesList;
private MessageArrayAdapter mAdapter;
private LoadMessagesTask mLoadMessagesTask;
private OnMessageListActionListener mListener;
// define callback interface
public interface OnMessageListActionListener {
public void onMessageInitialisation();
public void onMessageSelected(Message selectedMessage);
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// setting callback
mListener = (OnMessageListActionListener) activity;
mIsLastMessages = activity instanceof DashboardActivity;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
inflater.inflate(R.layout.fragment_listview, container);
mProgressView = inflater.inflate(R.layout.listrow_progress, null);
mEmptyView = inflater.inflate(R.layout.fragment_nodata, null);
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// instantiate loading task
mLoadMessagesTask = new LoadMessagesTask();
// instantiate list of messages
mMessagesList = new ArrayList<Message>();
mAdapter = new MessageArrayAdapter(getActivity(), mMessagesList);
setListAdapter(mAdapter);
}
@Override
public void onResume() {
mListener.onMessageInitialisation();
super.onResume();
}
public void onListItemClick(ListView l, View v, int position, long id) {
Message selectedMessage = (Message) getListAdapter().getItem(position);
mListener.onMessageSelected(selectedMessage);
super.onListItemClick(l, v, position, id);
}
/* public methods to load messages from host acitivity, etc... */
}
Solución
La solución tonta es guardar los fragmentos en onSaveInstanceState (de la actividad del host) con putFragment y obtenerlos en onCreate a través de getFragment. Pero todavía tengo la extraña sensación de que las cosas no deberían funcionar así ... Vea el código a continuación:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
getSupportFragmentManager()
.putFragment(outState, MessagesFragment.class.getName(), mMessagesFragment);
}
protected void onCreate(Bundle savedInstanceState) {
Logger.d("Dash onCreate");
super.onCreate(savedInstanceState);
...
// create fragments to use
if (savedInstanceState != null) {
mMessagesFragment = (MessagesFragment) getSupportFragmentManager().getFragment(
savedInstanceState, MessagesFragment.class.getName());
StreamsFragment.class.getName());
}
if (mMessagesFragment == null)
mMessagesFragment = new MessagesFragment();
...
}
Cuando el FragmentPagerAdapter
agrega un fragmento al FragmentManager, utiliza una etiqueta especial basada en la posición particular en la que se colocará el fragmento. FragmentPagerAdapter.getItem(int position)
solo se llama cuando no existe un fragmento para esa posición. Después de rotar, Android notará que ya creó / guardó un fragmento para esta posición en particular, por lo que simplemente intenta volver a conectarse con FragmentManager.findFragmentByTag()
, en lugar de crear uno nuevo. Todo esto es gratis cuando se utiliza el FragmentPagerAdapter
y es por eso que es habitual tener su código de inicialización de fragmentos dentro del getItem(int)
.
Incluso si no estuviéramos usando un FragmentPagerAdapter
, no es una buena idea crear un nuevo fragmento cada vez en Activity.onCreate(Bundle)
. Como ha notado, cuando se agrega un fragmento al FragmentManager, se volverá a crear para usted después de rotarlo y no es necesario volver a agregarlo. Hacerlo es una causa común de errores al trabajar con fragmentos.
Un enfoque habitual cuando se trabaja con fragmentos es este:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
CustomFragment fragment;
if (savedInstanceState != null) {
fragment = (CustomFragment) getSupportFragmentManager().findFragmentByTag("customtag");
} else {
fragment = new CustomFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container, fragment, "customtag").commit();
}
...
}
Cuando usamos un FragmentPagerAdapter
, renunciamos a la administración de fragmentos al adaptador y no tenemos que realizar los pasos anteriores. De forma predeterminada, solo cargará un Fragmento delante y detrás de la posición actual (aunque no los destruye a menos que esté utilizando FragmentStatePagerAdapter
). Esto está controlado por ViewPager.setOffscreenPageLimit(int) . Debido a esto, no se garantiza que los métodos de llamada directa en los fragmentos fuera del adaptador sean válidos, ya que es posible que ni siquiera estén vivos.
Para putFragment
, su solución para usar putFragment
para poder obtener una referencia después no es tan loca, y no es tan diferente a la forma normal de usar fragmentos de todos modos (arriba). Es difícil obtener una referencia, porque el adaptador lo agrega el fragmento y no usted personalmente. Solo asegúrese de que offscreenPageLimit
del offscreenPageLimit
la página offscreenPageLimit
la offscreenPageLimit
sea lo suficientemente alto como para cargar los fragmentos deseados en todo momento, ya que confía en que esté presente. Esto evita las capacidades de carga lenta del ViewPager, pero parece ser lo que usted desea para su aplicación.
Otro enfoque es anular FragmentPageAdapter.instantiateItem(View, int)
y guardar una referencia al fragmento devuelto por la súper llamada antes de devolverlo (tiene la lógica para encontrar el fragmento, si ya está presente).
Para una imagen más completa, eche un vistazo a algunas de las fuentes de FragmentPagerAdapter (corto) y ViewPager (largo).
Encontré otra solución relativamente fácil para tu pregunta.
Como puede ver en el código fuente de FragmentPagerAdapter , los fragmentos administrados por FragmentPagerAdapter
almacenan en el FragmentManager
bajo la etiqueta generada usando:
String tag="android:switcher:" + viewId + ":" + index;
El viewId
es el container.getId()
, el container
es su instancia de ViewPager
. El index
es la posición del fragmento. Por lo tanto, puede guardar la identificación del objeto en outState
:
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("viewpagerid" , mViewPager.getId() );
}
@Override
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
if (savedInstanceState != null)
viewpagerid=savedInstanceState.getInt("viewpagerid", -1 );
MyFragmentPagerAdapter titleAdapter = new MyFragmentPagerAdapter (getSupportFragmentManager() , this);
mViewPager = (ViewPager) findViewById(R.id.pager);
if (viewpagerid != -1 ){
mViewPager.setId(viewpagerid);
}else{
viewpagerid=mViewPager.getId();
}
mViewPager.setAdapter(titleAdapter);
Si desea comunicarse con este fragmento, puede obtenerlo desde FragmentManager
, como por ejemplo:
getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewpagerid + ":0")
Mi solución es muy ruda pero funciona: al ser mis fragmentos creados dinámicamente a partir de datos retenidos, simplemente PageAdapter
todos los fragmentos del PageAdapter
antes de llamar a super.onSaveInstanceState()
y luego los super.onSaveInstanceState()
crear al crear la actividad:
@Override
protected void onSaveInstanceState(Bundle outState) {
outState.putInt("viewpagerpos", mViewPager.getCurrentItem() );
mSectionsPagerAdapter.removeAllfragments();
super.onSaveInstanceState(outState);
}
No puedes eliminarlos en onDestroy()
, de lo contrario obtendrás esta excepción:
java.lang.IllegalStateException:
no se puede realizar esta acción después de onSaveInstanceState
Aquí el código en el adaptador de página:
public void removeAllfragments()
{
if ( mFragmentList != null ) {
for ( Fragment fragment : mFragmentList ) {
mFm.beginTransaction().remove(fragment).commit();
}
mFragmentList.clear();
notifyDataSetChanged();
}
}
Solo onCreate()
la página actual y la restauro en onCreate()
, después de que se hayan creado los fragmentos.
if (savedInstanceState != null)
mViewPager.setCurrentItem( savedInstanceState.getInt("viewpagerpos", 0 ) );
Para obtener los fragmentos después del cambio de orientación, debe usar .getTag ().
getSupportFragmentManager().findFragmentByTag("android:switcher:" + viewPagerId + ":" + positionOfItemInViewPager)
Para un poco más de manejo, escribí mi propia ArrayList para mi PageAdapter para obtener el fragmento por viewPagerId y FragmentClass en cualquier posición:
public class MyPageAdapter extends FragmentPagerAdapter implements Serializable {
private final String logTAG = MyPageAdapter.class.getName() + ".";
private ArrayList<MyPageBuilder> fragmentPages;
public MyPageAdapter(FragmentManager fm, ArrayList<MyPageBuilder> fragments) {
super(fm);
fragmentPages = fragments;
}
@Override
public Fragment getItem(int position) {
return this.fragmentPages.get(position).getFragment();
}
@Override
public CharSequence getPageTitle(int position) {
return this.fragmentPages.get(position).getPageTitle();
}
@Override
public int getCount() {
return this.fragmentPages.size();
}
public int getItemPosition(Object object) {
//benötigt, damit bei notifyDataSetChanged alle Fragemnts refrehsed werden
Log.d(logTAG, object.getClass().getName());
return POSITION_NONE;
}
public Fragment getFragment(int position) {
return getItem(position);
}
public String getTag(int position, int viewPagerId) {
//getSupportFragmentManager().findFragmentByTag("android:switcher:" + R.id.shares_detail_activity_viewpager + ":" + myViewPager.getCurrentItem())
return "android:switcher:" + viewPagerId + ":" + position;
}
public MyPageBuilder getPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
return new MyPageBuilder(pageTitle, icon, selectedIcon, frag);
}
public static class MyPageBuilder {
private Fragment fragment;
public Fragment getFragment() {
return fragment;
}
public void setFragment(Fragment fragment) {
this.fragment = fragment;
}
private String pageTitle;
public String getPageTitle() {
return pageTitle;
}
public void setPageTitle(String pageTitle) {
this.pageTitle = pageTitle;
}
private int icon;
public int getIconUnselected() {
return icon;
}
public void setIconUnselected(int iconUnselected) {
this.icon = iconUnselected;
}
private int iconSelected;
public int getIconSelected() {
return iconSelected;
}
public void setIconSelected(int iconSelected) {
this.iconSelected = iconSelected;
}
public MyPageBuilder(String pageTitle, int icon, int selectedIcon, Fragment frag) {
this.pageTitle = pageTitle;
this.icon = icon;
this.iconSelected = selectedIcon;
this.fragment = frag;
}
}
public static class MyPageArrayList extends ArrayList<MyPageBuilder> {
private final String logTAG = MyPageArrayList.class.getName() + ".";
public MyPageBuilder get(Class cls) {
// Fragment über FragmentClass holen
for (MyPageBuilder item : this) {
if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
return super.get(indexOf(item));
}
}
return null;
}
public String getTag(int viewPagerId, Class cls) {
// Tag des Fragment unabhängig vom State z.B. nach bei Orientation change
for (MyPageBuilder item : this) {
if (item.fragment.getClass().getName().equalsIgnoreCase(cls.getName())) {
return "android:switcher:" + viewPagerId + ":" + indexOf(item);
}
}
return null;
}
}
Así que simplemente crea una MyPageArrayList con los fragmentos:
myFragPages = new MyPageAdapter.MyPageArrayList();
myFragPages.add(new MyPageAdapter.MyPageBuilder(
getString(R.string.widget_config_data_frag),
R.drawable.ic_sd_storage_24dp,
R.drawable.ic_sd_storage_selected_24dp,
new WidgetDataFrag()));
myFragPages.add(new MyPageAdapter.MyPageBuilder(
getString(R.string.widget_config_color_frag),
R.drawable.ic_color_24dp,
R.drawable.ic_color_selected_24dp,
new WidgetColorFrag()));
myFragPages.add(new MyPageAdapter.MyPageBuilder(
getString(R.string.widget_config_textsize_frag),
R.drawable.ic_settings_widget_24dp,
R.drawable.ic_settings_selected_24dp,
new WidgetTextSizeFrag()));
y agregarlos al viewPager:
mAdapter = new MyPageAdapter(getSupportFragmentManager(), myFragPages);
myViewPager.setAdapter(mAdapter);
después de esto, puede obtener, después de la orientación, cambiar el fragmento correcto utilizando su clase:
WidgetDataFrag dataFragment = (WidgetDataFrag) getSupportFragmentManager()
.findFragmentByTag(myFragPages.getTag(myViewPager.getId(), WidgetDataFrag.class));
Quiero ofrecer una solución alternativa para tal vez un caso ligeramente diferente, ya que muchas de mis búsquedas de respuestas me llevaron a este hilo.
Mi caso : estoy creando / agregando páginas dinámicamente y deslizándolas en un ViewPager, pero cuando se gira (onConfigurationChange) termino con una nueva página porque, por supuesto, se vuelve a llamar a OnCreate. Pero quiero mantener la referencia a todas las páginas que se crearon antes de la rotación.
Problema : no tengo identificadores únicos para cada fragmento que creo, por lo que la única forma de hacer referencia fue almacenar de alguna manera las referencias en un Arreglo para restaurarlas después del cambio de rotación / configuración.
Solución alternativa : el concepto clave era que la Actividad (que muestra los Fragmentos) también administre la matriz de referencias a los Fragmentos existentes, ya que esta actividad puede utilizar Paquetes en onSaveInstanceState
public class MainActivity extends FragmentActivity
Así que dentro de esta actividad, declaro un miembro privado para rastrear las páginas abiertas
private List<Fragment> retainedPages = new ArrayList<Fragment>();
Esto se actualiza cada vez que se llama y se restaura onSaveInstanceState en onCreate
@Override
protected void onSaveInstanceState(Bundle outState) {
retainedPages = _adapter.exportList();
outState.putSerializable("retainedPages", (Serializable) retainedPages);
super.onSaveInstanceState(outState);
}
... así que una vez que se almacena, se puede recuperar ...
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState != null) {
retainedPages = (List<Fragment>) savedInstanceState.getSerializable("retainedPages");
}
_mViewPager = (CustomViewPager) findViewById(R.id.viewPager);
_adapter = new ViewPagerAdapter(getApplicationContext(), getSupportFragmentManager());
if (retainedPages.size() > 0) {
_adapter.importList(retainedPages);
}
_mViewPager.setAdapter(_adapter);
_mViewPager.setCurrentItem(_adapter.getCount()-1);
}
Estos fueron los cambios necesarios en la actividad principal, por lo que necesitaba los miembros y los métodos de mi FragmentPagerAdapter para que esto funcionara, así que dentro de
public class ViewPagerAdapter extends FragmentPagerAdapter
una construcción idéntica (como se muestra arriba en MainActivity)
private List<Fragment> _pages = new ArrayList<Fragment>();
y esta sincronización (como se usa anteriormente en onSaveInstanceState) es soportada específicamente por los métodos
public List<Fragment> exportList() {
return _pages;
}
public void importList(List<Fragment> savedPages) {
_pages = savedPages;
}
Y finalmente, en la clase de fragmentos.
public class CustomFragment extends Fragment
Para que todo esto funcione, hubo dos cambios, primero
public class CustomFragment extends Fragment implements Serializable
y luego agregar esto a onCreate para que los fragmentos no se destruyan
setRetainInstance(true);
Todavía estoy en el proceso de envolver mi cabeza alrededor de los fragmentos y el ciclo de vida de Android, por lo que la advertencia aquí es que puede haber redundancias / ineficiencias en este método. Pero funciona para mí y espero que pueda ser útil para otros con casos similares a los míos.
Quiero ofrecer una solución que amplíe la maravillosa respuesta de antonyt
y la mención de reemplazar FragmentPageAdapter.instantiateItem(View, int)
para guardar las referencias a los Fragments
creados para que pueda trabajar en ellos más adelante. Esto también debería funcionar con FragmentStatePagerAdapter
; ver las notas para más detalles.
Este es un ejemplo simple de cómo obtener una referencia a los Fragments
devueltos por FragmentPagerAdapter
que no se basan en las tags
internas establecidas en los Fragments
. La clave es anular instantiateItem()
y guardar referencias allí en lugar de en getItem()
.
public class SomeActivity extends Activity {
private FragmentA m1stFragment;
private FragmentB m2ndFragment;
// other code in your Activity...
private class CustomPagerAdapter extends FragmentPagerAdapter {
// other code in your custom FragmentPagerAdapter...
public CustomPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
// Do NOT try to save references to the Fragments in getItem(),
// because getItem() is not always called. If the Fragment
// was already created then it will be retrieved from the FragmentManger
// and not here (i.e. getItem() won''t be called again).
switch (position) {
case 0:
return new FragmentA();
case 1:
return new FragmentB();
default:
// This should never happen. Always account for each position above
return null;
}
}
// Here we can finally safely save a reference to the created
// Fragment, no matter where it came from (either getItem() or
// FragmentManger). Simply save the returned Fragment from
// super.instantiateItem() into an appropriate reference depending
// on the ViewPager position.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// save the appropriate reference depending on position
switch (position) {
case 0:
m1stFragment = (FragmentA) createdFragment;
break;
case 1:
m2ndFragment = (FragmentB) createdFragment;
break;
}
return createdFragment;
}
}
public void someMethod() {
// do work on the referenced Fragments, but first check if they
// even exist yet, otherwise you''ll get an NPE.
if (m1stFragment != null) {
// m1stFragment.doWork();
}
if (m2ndFragment != null) {
// m2ndFragment.doSomeWorkToo();
}
}
}
o si prefiere trabajar con tags
lugar de variables / referencias de miembros de clase a los Fragments
, también puede tomar las tags
establecidas por FragmentPagerAdapter
de la misma manera: NOTA: esto no se aplica a FragmentStatePagerAdapter
ya que no establece tags
al crear Sus Fragments
.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// get the tags set by FragmentPagerAdapter
switch (position) {
case 0:
String firstTag = createdFragment.getTag();
break;
case 1:
String secondTag = createdFragment.getTag();
break;
}
// ... save the tags somewhere so you can reference them later
return createdFragment;
}
Tenga en cuenta que este método NO se basa en imitar el conjunto de tag
internas de FragmentPagerAdapter
y, en su lugar, utiliza las API adecuadas para recuperarlas. De esta manera, incluso si la tag
cambia en futuras versiones de la SupportLibrary
de SupportLibrary
, todavía estará seguro.
No olvide que, dependiendo del diseño de su Activity
, los Fragments
que está intentando trabajar pueden existir o no, por lo que debe tenerlo en cuenta al realizar verificaciones null
antes de usar sus referencias.
Además, si en cambio está trabajando con FragmentStatePagerAdapter
, entonces no desea mantener referencias sólidas a sus Fragments
porque podría tener muchos de ellos y las referencias difíciles los mantendrían innecesariamente en la memoria. En su lugar, guarde las referencias de Fragment
en WeakReference
variables WeakReference
en lugar de las estándar. Me gusta esto:
WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
// reference hasn''t been cleared yet; do work...
}
Se me ocurrió esta solución simple y elegante. Se supone que la actividad es responsable de crear los Fragmentos, y el Adaptador solo los sirve.
Este es el código del adaptador (nada extraño aquí, excepto por el hecho de que mFragments
es una lista de fragmentos mantenidos por la Actividad)
class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
@Override
public int getCount() {
return mFragments.size();
}
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
@Override
public CharSequence getPageTitle(int position) {
TabFragment fragment = (TabFragment)mFragments.get(position);
return fragment.getTitle();
}
}
Todo el problema de este hilo es obtener una referencia de los fragmentos "antiguos", por lo que uso este código en onCreate de la Actividad.
if (savedInstanceState!=null) {
if (getSupportFragmentManager().getFragments()!=null) {
for (Fragment fragment : getSupportFragmentManager().getFragments()) {
mFragments.add(fragment);
}
}
}
Por supuesto, puede ajustar aún más este código si es necesario, por ejemplo, asegurándose de que los fragmentos sean instancias de una clase en particular.
Si alguien tiene problemas con su FragmentStatePagerAdapter que no restaura correctamente el estado de sus fragmentos ... es decir ... FragmentStatePagerAdager crea los nuevos fragmentos en lugar de que los restaure del estado ...
Asegúrese de llamar a ViewPager.setOffscreenPageLimit()
ANTES de llamar a ViewPager.setAdapeter(fragmentStatePagerAdapter)
Al llamar a ViewPager.setOffscreenPageLimit()
... ViewPager buscará inmediatamente su adaptador e intentará obtener sus fragmentos. Esto podría suceder antes de que ViewPager tenga la oportunidad de restaurar los Fragmentos desde savedInstanceState (creando así nuevos Fragmentos que no se pueden reinicializar desde SavedInstanceState porque son nuevos).
añadir:
@SuppressLint("ValidFragment")
antes de tu clase
No funciona hacer algo como esto:
@SuppressLint({ "ValidFragment", "HandlerLeak" })