ViewPager.setOffscreenPageLimit (0) no funciona como se esperaba

100

Los fragmentos que uso en mi ViewPagerinstancia consumen muchos recursos, por lo que solo me gustaría cargar uno a la vez. Cuando intento lo siguiente:

mViewPager.setOffscreenPageLimit(0);
mViewPager.setAdapter(mPagerAdapter);

Mi FragmentStatePagerAdapter.getItem(int position)función de anulación se llama 3 veces, que es lo que sucede cuando llamo mViewPager.setOffscreenPageLimit(1). Esperaría que solo se llame una vez, porque especifiqué 0 páginas fuera de la pantalla.

Creo que estoy llamando todo correctamente, porque si llamo mViewPager.setOffscreenPageLimit(2), FragmentStatePagerAdapter.getItem(int position)se llama 5 veces como esperaría.

¿ViewPager requiere un mínimo de 1 páginas fuera de la pantalla o estoy haciendo algo mal aquí?

Chris Lacy
fuente
9
Agregué
Mark
Solo tenía 3 pestañas y la configuración ViewPager.setOffscreenPageLimit(2)funcionó para mí. TryViewPager.setOffscreenPageLimit(totTabs - 1)
sibin

Respuestas:

112

¿ViewPager requiere un mínimo de 1 páginas fuera de la pantalla?

Si. Si estoy leyendo el código fuente correctamente, debería recibir una advertencia sobre esto en LogCat, algo como:

Requested offscreen page limit 0 too small; defaulting to 1
CommonsWare
fuente
6
¡Esta no es una solución! Solo quiero cargar 1 fragmento, no 2. ¿Seguramente esto es posible de alguna manera?
HGPB
14
@Haraldo: "¿Seguramente esto es posible de alguna manera?" - no con ViewPager. ViewPagercrea estos fragmentos para que existan las vistas, de modo que el usuario pueda deslizarse entre ellos, con el efecto animado que muestra la vista anterior deslizándose fuera de la pantalla y la nueva vista deslizándose hacia la pantalla. Le invitamos a intentar escribir el suyo propio ViewPagerque pueda deslizarse entre cosas que no existen. Puede leer más sobre esto en code.google.com/p/android/issues/detail?id=56667#c3
CommonsWare
2
Sí, he leído este número. Simplemente no es lógico. Espero ser capaz setOffscreenPageLimit(0)de 0. Entonces podré afrontar las consecuencias yo mismo. Un fragmento vacío predeterminado podría ser un marcador de posición, por ejemplo, hasta que se haya cargado el fragmento. Todo lo cual podría tener lugar fuera del hilo de la interfaz de usuario. De todos modos no lo he pensado mucho.
HGPB
5
@Haraldo: "Un fragmento vacío predeterminado podría ser un marcador de posición, por ejemplo, hasta que el fragmento se haya cargado". Puede tener un marcador de posición en su fragmento hoy, con la implementación existente de ViewPager, uno que reemplace con el contenido real cuando esté disponible. .
CommonsWare
2
onPageChangeListener parece la solución.
alicanbatur
123

La mejor manera que encontré fue setUserVisibleHint
agregar esto a su fragmento

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}
Tyler Davis
fuente
1
+++++ para esta solución !!! gracias, después de agregar este código obtuve NPL por primera vez, acabo de agregar el bloque try catch y funciona perfecto para mí.
Bhavin Chauhan
1
Al usar esto, no carga la fecha en el SecondViewPager Fragment, en el segundo fragmento sus datos de carga del tercer fragmento. ¿Alguien me puede ayudar?
Arshad
¿Me estoy perdiendo algo? SetUserVisibleHint llamado antes de onCreateView
Anton Makov
La mejor solución ahora está obsoleta
M.Sameer
@ M.Sameer ¿alguna alternativa para setUserVisibleHint ha quedado obsoleta?
Yudi karma
8

Puedes probar así:

public abstract class LazyFragment extends Fragment {
    protected boolean isVisible;
    /**
     * 在这里实现Fragment数据的缓加载.
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(getUserVisibleHint()) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }
    protected void onVisible(){
        lazyLoad();
    }
    protected abstract void lazyLoad();
    protected void onInvisible(){}

protected abstract void lazyLoad();
protected void onInvisible(){}
Terry
fuente
esa mejor respuesta!
Radesh
7

Primero agregar

   boolean isFragmentLoaded = false;

que

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser && !isFragmentLoaded) {
        //Load Your Data Here like.... new GetContacts().execute();

        isFragmentLoaded = true;
    }
else{
     }
}
Arpit J.
fuente
6
pero este método llama antes de onCreateView
AndroidGeek
2

ViewPager está predeterminado para cargar la página siguiente (Fragmento) que no puede cambiar con setOffscreenPageLimit (0). Pero puedes hacer algo para piratear. Puede implementar la función onPageSelected en Activity que contiene ViewPager. En el siguiente Fragmento (que no desea cargar), escribe una función, digamos showViewContent () donde coloca todo el código de inicio que consume recursos y no hace nada antes del método onResume (). Luego llame a la función showViewContent () dentro de onPageSelected. Espero que esto ayude.

Friends_little_black
fuente
1

Este puede ser un hilo antiguo, pero parece funcionar para mí. Anule esta función:

@Override
public void setMenuVisibility(boolean menuVisible) { 
    super.setMenuVisibility(menuVisible);

    if ( menuVisible ) {
        /**
         * Load your stuffs here.
         */
    } else  { 
        /**
         * Fragment not currently Visible.
         */
    } 
}

codificaciones felices ...

ralphgabb
fuente
¿Qué hay en webview? Le sugiero que cree un nuevo hilo para eso.
ralphgabb
1

en mi caso, quería iniciar algunas animaciones en las vistas, pero con setUserVisibleHint obtuve algunos problemas ...
mi solución es:

1 / addOnPageChangeListener para su adaptador:

mViewPager.addOnPageChangeListener(this);

2 / implementar OnPageChangeListener:

public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener

3 / anular los 3 métodos:

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{

}

@Override
public void onPageSelected(int position)
{ 
}

@Override
public void onPageScrollStateChanged(int state)
{
}

4 / declara e inicializa esta variable en tu clase

private static int mTabState = 1;

aviso : tengo tres fragmentos en mi adaptador, y uso mTabState para setCurrentItem y la posición actual del adaptador que reconoce qué fragmento se muestra al usuario a tiempo ... 5 / en el método onPageSelected agregue estos códigos:

if (mTabState == 0 || position == 0)
    {
        Intent intent = new Intent("animation");
        intent.putExtra("current_position", position);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
    }

si la página anterior o la página actual es la página 0 (fragmento en la posición 0), haga esto

6 / ahora en su clase de fragmento (fragmento en la posición 0 del adaptador), debe crear un receptor de transmisión y registrarlo en el método onResume y anular el registro en los métodos de pausa:

BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (Objects.equals(intent.getAction(), "animation"))
        {
            int currentPosition = intent.getIntExtra("current_position", 0);
            if (currentPosition == 0)
            {
                startAnimation();
                setViewsVisible();
            } else
            {
                setViewsInvisible();
            }
        }
    }
};

@Override
public void onResume()
{
    super.onResume();
    LocalBroadcastManager.getInstance(mContext).registerReceiver(broadcastReceiver, new IntentFilter("animation"));
}

@Override
public void onPause()
{
    super.onPause();
    LocalBroadcastManager.getInstance(mContext).unregisterReceiver(broadcastReceiver);
}

resumen: tengo un adaptador de buscapersonas de fragmentos que muestra tres fragmentos, quiero mostrar algunas animaciones en vistas en fragmentos en la posición 0 del adaptador, para esto utilizo BroadcastReceiver. Cuando se elige un fragmento, comienzo el método de animación y le muestro las vistas al usuario, cuando el fragmento no se muestra al usuario, intento ver las vistas invisibles ...

AREF
fuente
1

Pruebe este código para resolver el problema de actualización de la vista en Viewpager ...

/* DO NOT FORGET! The ViewPager requires at least “1” minimum OffscreenPageLimit */
int limit = (mAdapter.getCount() > 1 ? mAdapter.getCount() - 1 : 1);
mViewPager.setOffscreenPageLimit(limit);
Abhijeet Sharma
fuente
0

para la función "instantiateItem", simplemente prepare el fragmento, pero no cargue el contenido pesado.

Utilice "onPageChangeListener", de modo que cada vez que vaya a una página específica, cargue su contenido pesado y lo muestre.

desarrollador de Android
fuente
0

Tengo el mismo problema. Encontré un código útil en este sitio y lo transformé.

El mínimo int para mViewPager.setOffscreenPageLimit (...); es 1, por lo que incluso si lo cambia a 0, todavía tendrá 2 páginas cargadas.

Lo primero que debe hacer es crear un int estático que llamaremos a maxPageCount y anularemos el método FragmentStatePagerAdapter getCount () para devolver maxPageCount:

    @Override
    public int getCount() {
        return maxPageCount;
    }

Cree entonces un método estático accesible desde cualquier parte del programa que le permitirá cambiar este maxCount:

public static void addPage(){
    maxPageCount++; //or maxPageCount = fragmentPosition+2
    mFragmentStatePagerAdapter.notifyDataSetChanged(); //notifyDataSetChanged is important here.
}

Ahora inicialice maxPageCount en 1. Cuando quiera, puede agregar otra página. En mi caso, cuando necesitaba que el usuario tratara la página actual primero antes de generar la otra. Lo hace y luego, sin problema, puede pasar a la página siguiente.

Espero que ayude a alguien.

K20
fuente
0

Utilice este // crear un booleano para recuperar datos. Booleano privado isViewShown = false;

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (getView() != null) {
        isViewShown = true;
        // fetchdata() contains logic to show data when page is selected mostly asynctask to fill the data
        fetchData();
    } else {
        isViewShown = false;
    }
} 
Rohit Sharma
fuente