La vista de Fragmento de ViewPager se pierde cuando el fragmento padre de ViewPager se oculta y luego se muestra

He estado viendo un comportamiento extraño con mi ViewPager junto con mi propio FragmentStatePagerAdapter.

La jerarquía de Mi vista es la siguiente:

-> (1) Fragment root view (RelativeLayout) -> (2) ViewPager -> (3) ViewPager's current fragment view 

Cuando el fragmento que es responsable de la vista de raíz del fragmento (1) se oculta (usando .hide () en una transacción de fragmentos) y luego se muestra (con .show ()), la vista de fragmento que estaba mostrando en ViewPager ) Se convierte en nulo, aunque el fragmento todavía existe. Básicamente, mi ViewPager se vuelve completamente vacío / transparente.

La única manera que he encontrado para arreglar esto es llamar

 int current = myViewPager.getCurrentItem(); myViewPager.setAdapter(myAdapter); myViewPager.setCurrentItem(current); 

Después de que se muestre el fragmento padre. Esto de alguna manera provoca que las vistas sean recreadas y aparezcan en la pantalla. Desafortunadamente, esto ocasionalmente causa excepciones tratando con el adaptador de paginador llamando unregisterDataSetObserver() dos veces en un observador antiguo.

¿Hay una mejor manera de hacer esto? Supongo que lo que estoy pidiendo es:

¿Por qué mis vistas de fragmento dentro de ViewPager se destruyen cuando el fragmento padre del ViewPager está oculto?

Actualización: esto también sucede cuando la aplicación es "minimizada" y luego "restaurada" (presionando la tecla de acción de inicio y luego volver).

Por solicitud, aquí está mi clase de adaptador de buscapersonas:

 public class MyInfoSlidePagerAdapter extends FragmentStatePagerAdapter { private ArrayList<MyInfo> infos = new ArrayList<MyInfo>(); public MyInfoSlidePagerAdapter (FragmentManager fm) { super(fm); } public MyInfoSlidePagerAdapter (FragmentManager fm, MyInfo[] newInfos) { super(fm); setInfos(newInfos); } @Override public int getItemPosition(Object object) { int position = infos.indexOf(((MyInfoDetailsFragment)object).getMyInfo()); return position > 0 ? position : POSITION_NONE; } @Override public CharSequence getPageTitle(int position) { return infos.get(position).getName(); } @Override public Fragment getItem(int i) { return infos.size() > 0 ? MyInfoDetailsFragment.getNewInstance(infos.get(i)) : null; } @Override public int getCount() { return infos.size(); } public Location getMyInfoAtPosition(int i) { return infos.get(i); } public void setInfos(MyInfo[] newInfos) { infos = new ArrayList<MyInfo>(Arrays.asList(newInfos)); } public int getPositionOfMyInfo(MyInfo info) { return infos.indexOf(info); } } 

He cambiado el nombre de algunas variables, pero aparte de eso es exactamente lo que tengo.

No estás proporcionando información suficiente para tu problema específico, así que construí un proyecto de ejemplo que intenta reproducir tu problema: la aplicación tiene una actividad que contiene un fragmento ( PagerFragment ) dentro de un diseño relativo y debajo de este diseño Tengo un botón que PagerFragment y demostraciones sobre PagerFragment . PagerFragment tiene un ViewPager y cada fragmento en el adaptador de paginador simplemente muestra una etiqueta – este fragmento se denomina DataFragment . La lista de etiquetas se crea en la actividad principal y se pasa a PagerFragment ya continuación a través de su adaptador a cada DataFragment . Cambiar la visibilidad de PagerFragment se realiza sin problemas y cada vez que se vuelve visible de nuevo muestra la etiqueta mostrada anteriormente.

La clave del problema: Use Fragment # getChildFragmentManager () cuando esté creando el adaptador de viewpager y no getFragmentManager!

Tal vez usted puede comparar este simple proyecto con lo que tiene y comprobar dónde están las diferencias. Así que aquí va (de arriba hacia abajo):

PagerActivity (la única actividad en el proyecto):

 public class PagerActivity extends FragmentActivity { private static final String PAGER_TAG = "PagerActivity.PAGER_TAG"; @Override protected void onCreate(Bundle savedInstance) { super.onCreate(savedInstance); setContentView(R.layout.pager_activity); if (savedInstance == null) { PagerFragment frag = PagerFragment.newInstance(buildPagerData()); FragmentManager fm = getSupportFragmentManager(); fm.beginTransaction().add(R.id.layout_fragments, frag, PAGER_TAG).commit(); } findViewById(R.id.btnFragments).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { changeFragmentVisibility(); } }); } private List<String> buildPagerData() { ArrayList<String> pagerData = new ArrayList<String>(); pagerData.add("Robert de Niro"); pagerData.add("John Smith"); pagerData.add("Valerie Irons"); pagerData.add("Metallica"); pagerData.add("Rammstein"); pagerData.add("Zinedine Zidane"); pagerData.add("Ronaldo da Lima"); return pagerData; } protected void changeFragmentVisibility() { Fragment frag = getSupportFragmentManager().findFragmentByTag(PAGER_TAG); if (frag == null) { Toast.makeText(this, "No PAGER fragment found", Toast.LENGTH_SHORT).show(); return; } boolean visible = frag.isVisible(); Log.d("APSampler", "Pager fragment visibility: " + visible); FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); if (visible) { ft.hide(frag); } else { ft.show(frag); } ft.commit(); getSupportFragmentManager().executePendingTransactions(); } } 

Su archivo de diseño pager_activity.xml

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="4dp" > <Button android:id="@+id/btnFragments" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="Hide/Show fragments" /> <RelativeLayout android:id="@+id/layout_fragments" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_above="@+id/btnFragments" android:layout_marginBottom="4dp" > </RelativeLayout> </RelativeLayout> 

Observe que estoy agregando el PagerFragment cuando se muestra la actividad por primera vez – y la clase PagerFragment :

 public class PagerFragment extends Fragment { private static final String DATA_ARGS_KEY = "PagerFragment.DATA_ARGS_KEY"; private List<String> data; private ViewPager pagerData; public static PagerFragment newInstance(List<String> data) { PagerFragment pagerFragment = new PagerFragment(); Bundle args = new Bundle(); ArrayList<String> argsValue = new ArrayList<String>(data); args.putStringArrayList(DATA_ARGS_KEY, argsValue); pagerFragment.setArguments(args); return pagerFragment; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); data = getArguments().getStringArrayList(DATA_ARGS_KEY); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.pager_fragment, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { pagerData = (ViewPager) view.findViewById(R.id.pager_data); setupPagerData(); } private void setupPagerData() { PagerAdapter adapter = new LocalPagerAdapter(getChildFragmentManager(), data); pagerData.setAdapter(adapter); } } 

Su diseño (sólo el ViewPager que toma en tamaño completo):

 <android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pager_data" android:layout_width="match_parent" android:layout_height="match_parent" /> 

Y su adaptador:

 public class LocalPagerAdapter extends FragmentStatePagerAdapter { private List<String> pagerData; public LocalPagerAdapter(FragmentManager fm, List<String> pagerData) { super(fm); this.pagerData = pagerData; } @Override public Fragment getItem(int position) { return DataFragment.newInstance(pagerData.get(position)); } @Override public int getCount() { return pagerData.size(); } } 

Este adaptador crea un DataFragment para cada página:

 public class DataFragment extends Fragment { private static final String DATA_ARG_KEY = "DataFragment.DATA_ARG_KEY"; private String localData; public static DataFragment newInstance(String data) { DataFragment df = new DataFragment(); Bundle args = new Bundle(); args.putString(DATA_ARG_KEY, data); df.setArguments(args); return df; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); localData = getArguments().getString(DATA_ARG_KEY); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { return inflater.inflate(R.layout.data_fragment, container, false); } @Override public void onViewCreated(View view, Bundle savedInstanceState) { view.findViewById(R.id.btn_page_action).setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getActivity(), localData, Toast.LENGTH_SHORT).show(); } }); ((TextView) view.findViewById(R.id.txt_label)).setText(localData); } } 

DataFragment el diseño de DataFragment :

 <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="4dp" > <Button android:id="@+id/btn_page_action" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" android:text="Interogate" /> <TextView android:id="@+id/txt_label" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:textAppearance="?android:attr/textAppearanceLarge" /> </RelativeLayout> 

¡Disfruta de la codificación!

Tal vez ayudará a mViewPager.setOffscreenPageLimit (5)

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.

Esto se ofrece como una optimización. Si conoce de antemano el número de páginas que necesitará para soportar o tiene mecanismos de carga por descarga en sus páginas, ajustar esta configuración puede tener beneficios en la suavidad percibida de las animaciones de paginación y la interacción. Si tiene un número pequeño de páginas (3-4) que puede mantener activas todas a la vez, se gastará menos tiempo en el diseño para los subárboles de vista recién creados que las páginas de usuario de un lado a otro.

Debe mantener este límite bajo, especialmente si sus páginas tienen diseños complejos. Esta configuración predeterminada es 1.

Ver Pager es bastante inflexible en mantener el mantenimiento de sus Fragmentos frescos siempre y así optimizar el rendimiento liberando memoria cuando no se utiliza un fragmento. Claramente eso es un rasgo útil válido en un sistema móvil. Pero debido a esta desasignación persistente de recursos el fragmento se crea cada vez que se centra.

 mViewPager.setOffscreenPageLimit(NUMBEROFFRAGMENTSCREENS); 

Aquí está la documentación.

Este viejo poste tiene una solución interesante para su problema.

Yo tuve el mismo problema. Mi aplicación (FragmentActivity) tiene un buscapersonas (ViewPager) con 3 fragmentos. Mientras se desliza entre los fragmentos son destruidos y recreados todo el tiempo. En realidad no hace ningún problema en la funcionalidad (esperar Cursores no cerrado), pero también me estaba preguntando sobre esta cuestión.

No sé si hay una solución para cambiar el comportamiento de ViewPager, pero sugiero tener un objeto de configuración (tal vez una estática en) y antes de destruir guardar el objeto myViewPager en el objeto de configuración.

 public class App extends FragmentActivity { static MyData data; @Override protected void onCreate(Bundle savedInstanceState) { data = (MyData) getLastCustomNonConfigurationInstance(); if (data == null) { data = new MyData(); data.savedViewPager = myViewPager; } else { myViewPager = data.savedViewPager; } } @Override public Object onRetainCustomNonConfigurationInstance() { Log.d("onRetainCustomNonConfigurationInstance", "Configuration call"); return data; } } public class MyData { public ViewPager savedViewPager; } 

De esta manera, puede guardar la referencia a un objeto que no se destruirá por lo que hay referencia a él y puede recargar todos sus objetos cruciales.

¡Espero que encuentres mi sugerencia útil!

  • ¿Cómo saber qué fragmento está seleccionado actualmente en un ViewPager?
  • ANDROID: ViewPager con diferentes fragmentos en el método instantiateItem
  • Cómo personalizar PagerTabStrip
  • Confusión en 'posición' en la función instantiateItem en la clase PagerAdapter (android)
  • Adaptador de cambio de viewpager android
  • NestedScrollView dentro de ViewPager dentro de NestedScrollView: NestedScrollView Inferior que no se desplaza
  • Android.app Fragments vs. android.support.v4.app usando ViewPager?
  • Cómo obtener una indicación de que el usuario está intentando desplazarse más en un ViewPager
  • Cómo comprometer Fragment transacciones en FragmentPagerAdapter en onLoadFinish método de devolución de llamada?
  • Ver ciclo de vida del paginador y del fragmento
  • Cambio de la imagen de fondo del fragmento actual en viewpager
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.