Cómo determinar cuándo Fragmento se vuelve visible en ViewPager

Problema: Fragment onResume() en ViewPager se dispara antes de que el fragmento se vuelva realmente visible.

Por ejemplo, tengo 2 fragmentos con ViewPager y FragmentPagerAdapter . El segundo fragmento sólo está disponible para usuarios autorizados y necesito pedir al usuario que inicie sesión cuando el fragmento se vuelva visible (mediante un cuadro de diálogo de alerta).

PERO el ViewPager crea el segundo fragmento cuando el primero es visible para almacenar en caché el segundo fragmento y lo hace visible cuando el usuario comienza a pasar.

Por lo tanto, el evento onResume() en el segundo fragmento mucho antes de que se haga visible. Es por eso que estoy tratando de encontrar un evento que se activa cuando el segundo fragmento se hace visible para mostrar un diálogo en el momento adecuado.

¿Cómo se puede hacer esto?

Ahora, si usas la biblioteca de soporte para fragmentos, puedes usar getUserVisibleHint() o reemplazar setUserVisibleHint() para capturar los cambios como se describe en la respuesta de gorn.

UPDATE 1 Aquí hay un pequeño problema con getUserVisibleHint() . Este valor es por defecto true .

 // Hint provided by the app that this fragment is currently visible to the user. boolean mUserVisibleHint = true; 

Por lo tanto, puede haber un problema cuando intenta utilizarlo antes de setUserVisibleHint() . Como solución, puede establecer valor en el método onCreate como este.

 public void onCreate(@Nullable Bundle savedInstanceState) { setUserVisibleHint(false); 

La respuesta anticuada:

En la mayoría de los casos de uso, ViewPager sólo muestra una página a la vez, pero los fragmentos pre-caché también se ponen al estado "visible" (en realidad invisible) si está utilizando FragmentStatePagerAdapter en la Android Support Library pre-r11 .

Anular:

 public class MyFragment extends Fragment { @Override public void setMenuVisibility(final boolean visible) { super.setMenuVisibility(visible); if (visible) { // ... } } // ... } 

Para capturar el estado de foco de fragmento, que creo que es el estado más adecuado de la "visibilidad" que quiere decir, ya que sólo un fragmento en ViewPager realmente puede colocar sus elementos de menú junto con los elementos de la actividad de los padres.

Cómo determinar cuándo Fragmento se vuelve visible en ViewPager

Puede hacer lo siguiente por sobreescribir setUserVisibleHint en su Fragment :

 public class MyFragment extends Fragment { @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { } else { } } } 

Esto parece restaurar el onResume() normal onResume() que usted esperaría. Se juega bien con presionar la tecla de inicio para salir de la aplicación y luego volver a entrar en la aplicación. onResume() no se llama dos veces en una fila.

 @Override public void setUserVisibleHint(boolean visible) { super.setUserVisibleHint(visible); if (visible && isResumed()) { //Only manually call onResume if fragment is already visible //Otherwise allow natural fragment lifecycle to call onResume onResume(); } } @Override public void onResume() { super.onResume(); if (!getUserVisibleHint()) { return; } //INSERT CUSTOM CODE HERE } 

Aquí hay otra manera de usar onPageChangeListener :

  ViewPager pager = (ViewPager) findByViewId(R.id.viewpager); FragmentPagerAdapter adapter = new FragmentPageAdapter(getFragmentManager); pager.setAdapter(adapter); pager.setOnPageChangeListener(new OnPageChangeListener() { public void onPageSelected(int pageNumber) { // Just define a callback method in your fragment and call it like this! adapter.getItem(pageNumber).imVisible(); } public void onPageScrolled(int arg0, float arg1, int arg2) { // TODO Auto-generated method stub } public void onPageScrollStateChanged(int arg0) { // TODO Auto-generated method stub } }); 

setUserVisibleHint() se llama a veces antes de onCreateView() ya veces después de lo cual causa problemas.

Para superar esto es necesario comprobar isResumed() así como dentro del método setUserVisibleHint() . Pero en este caso me di cuenta setUserVisibleHint() se llama sólo si Fragmento se reanuda y visible, NO cuando se crea.

Por lo tanto, si desea actualizar algo cuando Fragment es visible , ponga su función de actualización en onCreate() y setUserVisibleHint() :

 @Override public View onCreateView(...){ ... myUIUpdate(); ... } .... @Override public void setUserVisibleHint(boolean visible){ super.setUserVisibleHint(visible); if (visible && isResumed()){ myUIUpdate(); } } 

UPDATE: Aún me di cuenta que myUIUpdate() se llama dos veces a veces, la razón es, si tiene 3 pestañas y este código está en la 2 ª pestaña, cuando abre la primera pestaña, la segunda pestaña también se crea incluso no es visible y myUIUpdate() Se llama. Luego, cuando pase a la segunda pestaña, se myUIUpdate() desde if (visible && isResumed()) y, como resultado, myUIUpdate() puede llamarse dos veces en un segundo.

El otro problema es !visible en setUserVisibleHint se llama tanto 1) cuando salga de la pantalla de fragmentos y 2) antes de que se crea, cuando se cambia a pantalla de fragmentos por primera vez.

Solución:

 private boolean fragmentResume=false; private boolean fragmentVisible=false; private boolean fragmentOnCreated=false; ... @Override public View onCreateView(...){ ... //Initialize variables if (!fragmentResume && fragmentVisible){ //only when first time fragment is created myUIUpdate(); } ... } @Override public void setUserVisibleHint(boolean visible){ super.setUserVisibleHint(visible); if (visible && isResumed()){ // only at fragment screen is resumed fragmentResume=true; fragmentVisible=false; fragmentOnCreated=true; myUIUpdate(); }else if (visible){ // only at fragment onCreated fragmentResume=false; fragmentVisible=true; fragmentOnCreated=true; } else if(!visible && fragmentOnCreated){// only when you go out of fragment screen fragmentVisible=false; fragmentResume=false; } } 

Explicación:

fragmentResume , fragmentVisible : Asegura que myUIUpdate() en onCreateView() se llama sólo cuando el fragmento es creado y visible, no en el reanudar. También resuelve el problema cuando estás en la primera pestaña, la segunda pestaña se crea aunque no sea visible. Esto resuelve eso y comprueba si la pantalla de fragmentos es visible cuando se onCreate .

fragmentOnCreated : Asegura que el fragmento no es visible y no se llama cuando se crea el fragmento por primera vez. Así que ahora esta cláusula if sólo se llama cuando se pasa de fragmento.

Actualización Puede poner todo este código en código BaseFragment como este y el método de anulación.

setPrimaryItem() en la subclase FragmentPagerAdapter . Yo uso este método, y funciona bien.

 @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { // This is what calls setMenuVisibility() on the fragments super.setPrimaryItem(container, position, object); if (object instanceof MyWhizBangFragment) { MyWhizBangFragment fragment = (MyWhizBangFragment) object; fragment.doTheThingYouNeedToDoOnBecomingVisible(); } } 
 package com.example.com.ui.fragment; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import com.example.com.R; public class SubscribeFragment extends Fragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_subscribe, container, false); return view; } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser) { // called here } } @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); } } 

Anule Fragment.onHiddenChanged() para ello.

public void onHiddenChanged(boolean hidden)

Se llama cuando el estado oculto (como devuelto por isHidden() ) del fragmento ha cambiado. Los fragmentos empiezan no ocultos; Esto se llamará siempre que el fragmento cambie de estado a partir de eso.

Parámetros
hiddenboolean : True si el fragmento está oculto, false si no es visible.

Me imaginé que onCreateOptionsMenu y onPrepareOptionsMenu métodos llamados sólo en el caso del fragmento realmente visible. No pude encontrar ningún método que se comporta como estos, también he intentado OnPageChangeListener pero no funcionó para las situaciones, por ejemplo, necesito una variable inicializada en el método onCreate .

Así que estos dos métodos se pueden utilizar para este problema como una solución, específicamente para trabajos pequeños y cortos.

Creo que esta es la mejor solución pero no la mejor. Voy a utilizar esto, pero esperar a una mejor solución al mismo tiempo.

Saludos.

Otra solución publicada aquí sobrecargando setPrimaryItem en el pageradapter de kris larson casi funcionó para mí. Pero este método se llama varias veces para cada configuración. También obtuve NPE desde puntos de vista, etc. en el fragmento ya que esto no está listo las primeras veces que se llama a este método. Con los siguientes cambios esto funcionó para mí:

 private int mCurrentPosition = -1; @Override public void setPrimaryItem(ViewGroup container, int position, Object object) { super.setPrimaryItem(container, position, object); if (position == mCurrentPosition) { return; } if (object instanceof MyWhizBangFragment) { MyWhizBangFragment fragment = (MyWhizBangFragment) object; if (fragment.isResumed()) { mCurrentPosition = position; fragment.doTheThingYouNeedToDoOnBecomingVisible(); } } } 

Añada el siguiente código inside fragment

 @Override public void setMenuVisibility(final boolean visible) { super.setMenuVisibility(visible); if (visible && isResumed()) { } } 

Tenga en cuenta que setUserVisibleHint(false) no se llama en la parada de actividad / fragmento. Usted todavía tendrá que comprobar inicio / parada para register/unregister correctamente register/unregister oyentes / etc.

Además, obtendrá setUserVisibleHint(false) si su fragmento comienza en un estado no visible; Usted no desea unregister su unregister allí ya que nunca se ha registrado antes en ese caso.

 @Override public void onStart() { super.onStart(); if (getUserVisibleHint()) { // register } } @Override public void onStop() { if (getUserVisibleHint()) { // unregister } super.onStop(); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser && isResumed()) { // register if (!mHasBeenVisible) { mHasBeenVisible = true; } } else if (mHasBeenVisible){ // unregister } } 

Encontré este problema cuando intentaba conseguir un temporizador para encender cuando el fragmento en el viewpager estaba en la pantalla para que el usuario vea.

El temporizador comenzó siempre justo antes de que el fragmento fuera visto por el usuario. Esto se debe a que el método onResume() en el fragmento se llama antes de que podamos ver el fragmento.

Mi solución fue hacer una comprobación en el método onResume() . Quería llamar a un cierto método 'foo ()' cuando el fragmento 8 era el paginador de la vista actual fragmento.

 @Override public void onResume() { super.onResume(); if(viewPager.getCurrentItem() == 8){ foo(); //Your code here. Executed when fragment is seen by user. } } 

Espero que esto ayude. He visto este problema surgir mucho. Esta parece ser la solución más simple que he visto. Muchos otros no son compatibles con API más bajos, etc.

Tuve el mismo problema. ViewPager ejecuta otros eventos del ciclo de vida de los fragmentos y no pude cambiar ese comportamiento. Escribí un paginador simple usando fragmentos y animaciones disponibles. SimplePager

 public abstract class FragmentHelpLoadDataWhenVisible extends Fragment { @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,Bundle savedInstanceState) { if(getUserVisibleHint()){ // fragment is visible loadData(); } return super.onCreateView(inflater, container, savedInstanceState); } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (isVisibleToUser && isResumed()) { // fragment is visible and have created loadData(); } } public void loadData(){ // data for fragment when it visible here } } 

Necesitas loadData() dentro de setUserVisibleHint y onCreateView porque setUserVisibleHint puede llamar antes de onCreateView así que si siempre loadData() dentro de setUserVisibleHint , podemos obtener NullPointerException

Por ejemplo, mi viewpager tiene 3 pestañas (Tab1, Tab2, Tab3).
En la primera vez voy a este viewpager, la función llamará en orden como

 Tab1 => setUserVisibleHint (isVisibleToUser = false) (isResumed = false) Tab2 => setUserVisibleHint (isVisibleToUser = false) (isResumed = false) Tab1 => setUserVisibleHint (isVisibleToUser = true) (isResumed = false) Tab1 => onCreateView (getUserVisibleHint() = true) Tab2 => onCreateView (getUserVisibleHint() = false) 

En este caso, loadData() se llama dentro onCreateView de Tab1 (no vamos a obtener NullPointerException )

Entonces voy a Tab2, la función llamará en orden como

 Tab3 => setUserVisibleHint (isVisibleToUser = false) (isResumed = false) Tab1 => setUserVisibleHint (isVisibleToUser = false) (isResumed = true) Tab2 => setUserVisibleHint (isVisibleToUser = true) (isResumed = true) Tab3 => onCreateView (getUserVisibleHint() = false) 

En este caso loadData() se llama dentro de setUserVisibleHint de Tab2 como esperábamos

Tenemos un caso especial con MVP donde el fragmento necesita notificar al presentador que la vista se ha hecho visible, y el presentador es inyectado por Dagger en el fragment.onAttach() .

setUserVisibleHint() no es suficiente, hemos detectado 3 casos diferentes que deben ser abordados ( onAttach() se menciona para que sepa cuando el presentador está disponible):

  1. Fragmento acaba de ser creado. El sistema realiza las siguientes llamadas:

     setUserVisibleHint() // before fragment's lifecycle calls, so presenter is null onAttach() ... onResume() 
  2. Fragmento ya creado y se presiona el botón de inicio. Al restaurar la aplicación en primer plano, se denomina:

     onResume() 
  3. Cambio de orientación:

     onAttach() // presenter available onResume() setUserVisibleHint() 

Sólo queremos que la pista de visibilidad llegue al presentador una vez, así que así es como lo hacemos:

 @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_list, container, false); setHasOptionsMenu(true); if (savedInstanceState != null) { lastOrientation = savedInstanceState.getInt(STATE_LAST_ORIENTATION, getResources().getConfiguration().orientation); } else { lastOrientation = getResources().getConfiguration().orientation; } return root; } @Override public void onResume() { super.onResume(); presenter.onResume(); int orientation = getResources().getConfiguration().orientation; if (orientation == lastOrientation) { if (getUserVisibleHint()) { presenter.onViewBecomesVisible(); } } lastOrientation = orientation; } @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); if (presenter != null && isResumed() && isVisibleToUser) { presenter.onViewBecomesVisible(); } } @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt(STATE_LAST_ORIENTATION, lastOrientation); } 

Encontré el mismo problema mientras trabajaba con FragmentStatePagerAdapters y 3 pestañas. Tuve que mostrar un Dilaog cada vez que se hizo clic en la primera pestaña y ocultarlo al hacer clic en otras pestañas.

La setUserVisibleHint() por sí sola no ayudó a encontrar el fragmento visible actual.

Al hacer clic en la 3ª pestaña —–> 1ª pestaña. Se disparó dos veces para el segundo fragmento y para el primer fragmento. Lo combiné con el método isResumed ().

  @Override public void setUserVisibleHint(boolean isVisibleToUser) { super.setUserVisibleHint(isVisibleToUser); isVisible = isVisibleToUser; // Make sure that fragment is currently visible if (!isVisible && isResumed()) { // Call code when Fragment not visible } else if (isVisible && isResumed()) { // Call code when Fragment becomes visible. } } 

En el fragmento usar el GestureDetector.OnGestureListenerand y cuando se enciende sabrás que "este" Fragmento es visible

En el fragmento use el Detector de Gestos

Puedo manejar el estado visible aquí: Comprobar fragmento está visible o no en android? . Divido el tipo de conmutador del Fragmento de tres maneras, y si el uso anidado, manejar el estado visible del fragmento secundario en su fragmento padre. Funciona para mí.

He anulado el método Count del FragmentStatePagerAdapter asociado y tengo que devolver el recuento total menos el número de páginas a ocultar:

  public class MyAdapter : Android.Support.V13.App.FragmentStatePagerAdapter { private List<Fragment> _fragments; public int TrimmedPages { get; set; } public MyAdapter(Android.App.FragmentManager fm) : base(fm) { } public MyAdapter(Android.App.FragmentManager fm, List<Android.App.Fragment> fragments) : base(fm) { _fragments = fragments; TrimmedPages = 0; } public override int Count { //get { return _fragments.Count; } get { return _fragments.Count - TrimmedPages; } } } 

Por lo tanto, si hay 3 fragmentos añadidos inicialmente al ViewPager y solo se mostrarán los primeros 2 hasta que se cumpla alguna condición, anule el recuento de páginas estableciendo TrimmedPages en 1 y solo se mostrarán las dos primeras páginas.

Esto funciona bien para las páginas en el final, pero no le ayudará realmente para los que están en el principio o en el medio (aunque hay un montón de maneras de hacer esto).

  • Desplazamiento no funciona en android ViewPager con FragmentPagerAdapter
  • Cambiar el nombre de página en Android Viewpager
  • Fragmento de niño que reemplaza el diseño de raíz de fragmento padre
  • ViewPager en ScrollView
  • FragmentPagerAdapter no está trabajando en la recreación del fragmento
  • Acceso a TextView en ViewPager desde la actividad
  • ViewPagers en un ListView mostrando elementos de la lista en blanco
  • Cómo forzar a ViewPager a volver a instanciar sus elementos
  • Llamar setHasOptionsMenu (true) de un fragmento da como resultado múltiples llamadas a onCreateOptionsMenu en Activity
  • ¿Cómo recargar / refrescar el fragmento que ya se ha mostrado una vez?
  • El método getChildFragmentManager () no está definido
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.