ViewPager.setOffscreenPageLimit (0) non funziona come previsto


100

I frammenti che uso nella mia ViewPageristanza richiedono molte risorse, quindi vorrei caricarne solo uno alla volta. Quando provo quanto segue:

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

La mia FragmentStatePagerAdapter.getItem(int position)funzione di override viene chiamata 3 volte, che è ciò che accade quando chiamo mViewPager.setOffscreenPageLimit(1). Mi aspetto che venga chiamato solo una volta, perché ho specificato 0 pagine fuori schermo.

Credo di chiamare tutto correttamente, perché se chiamo mViewPager.setOffscreenPageLimit(2), FragmentStatePagerAdapter.getItem(int position)viene chiamato 5 volte come mi aspetterei.

ViewPager richiede almeno 1 pagine fuori schermo o sto facendo qualcosa di sbagliato qui?


9
Ho aggiunto una richiesta di funzionalità: code.google.com/p/android/issues/detail?id=56667
Contrassegna il


Avevo solo 3 schede e l'impostazione ha ViewPager.setOffscreenPageLimit(2)funzionato per me. ProvaViewPager.setOffscreenPageLimit(totTabs - 1)
fratello

Risposte:


112

ViewPager richiede un minimo di 1 pagine fuori schermo

Sì. Se sto leggendo correttamente il codice sorgente, dovresti ricevere un avviso su questo in LogCat, qualcosa del tipo:

Requested offscreen page limit 0 too small; defaulting to 1

6
Questa non è una soluzione! Voglio caricare solo 1 frammento, non 2. Sicuramente questo è possibile in qualche modo?
HGPB

14
@Haraldo: "Sicuramente questo è possibile in qualche modo?" - non con ViewPager. ViewPagercrea questi frammenti in modo che le viste esistano, in modo che l'utente possa scorrere tra di loro, con l'effetto animato che mostra la vecchia vista che scorre fuori dallo schermo e la nuova vista che scorre sullo schermo. Sei libero di provare a scrivere il tuo ViewPagerche può scorrere tra le cose che non esistono. Puoi leggere ulteriori informazioni su questo su code.google.com/p/android/issues/detail?id=56667#c3
CommonsWare

2
Sì, ho letto questo numero. Semplicemente non è logico. Mi aspetto di essere in grado setOffscreenPageLimit(0)di 0. Allora posso affrontare le conseguenze da solo. Un frammento vuoto predefinito potrebbe essere un segnaposto per esempio fino a quando i frammenti non vengono caricati. Tutto ciò potrebbe avvenire fuori dal thread dell'interfaccia utente. Comunque non ci ho pensato molto.
HGPB

5
@Haraldo: "Un frammento vuoto predefinito potrebbe essere un segnaposto per esempio fino a quando il frammento non è stato caricato" - sei il benvenuto ad avere un segnaposto nel tuo frammento oggi, con l'implementazione esistente di ViewPager, uno che sostituisci con il contenuto reale quando disponibile .
CommonsWare

2
onPageChangeListener sembra la soluzione.
alicanbatur

123

Il modo migliore che ho trovato è stato setUserVisibleHint
aggiungerlo al tuo frammento

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}

1
+++++ per questa soluzione !!! grazie, dopo aver aggiunto questo codice ho ottenuto NPL per la prima volta, ho appena aggiunto try catch block e funziona perfettamente per me !!!
Bhavin Chauhan

1
Usando questo non carica la data su secondViewPager Fragment, sul secondo frammento i suoi dati di caricamento del terzo frammento. Qualcuno mi può aiutare?
Arshad

Mi manca qualcosa, setUserVisibleHint chiamato prima di onCreateView
Anton Makov

La soluzione migliore è ora deprecata
M.Sameer

@ M.Sameer qualche alternativa per setUserVisibleHint ha deprectate?
Yudi karma

8

Puoi provare in questo modo:

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(){}

quella migliore risposta!
Radesh

7

Primo Aggiungi

   boolean isFragmentLoaded = false;

di

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

        isFragmentLoaded = true;
    }
else{
     }
}

6
ma questo metodo chiama prima di onCreateView
AndroidGeek

2

ViewPager è l'impostazione predefinita per caricare la pagina successiva (Fragment) che non è possibile modificare con setOffscreenPageLimit (0). Ma puoi fare qualcosa per hackerare. È possibile implementare la funzione onPageSelected in Activity contenente ViewPager. Nel prossimo frammento (che non vuoi caricare), scrivi una funzione diciamo showViewContent () in cui inserisci tutto il codice di inizializzazione che consuma risorse e non fai nulla prima del metodo onResume (). Quindi chiama la funzione showViewContent () all'interno di onPageSelected. Spero che questo ti aiuti.


1

questo potrebbe essere un vecchio thread, ma sembra funzionare per me. Ignora questa funzione:

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

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

codifiche felici ...


cosa c'è su webview? Ti suggerisco di creare un nuovo thread per questo.
ralphgabb

1

nel mio caso volevo avviare alcune animazioni nelle viste, ma con setUserVisibleHint ho avuto alcuni problemi ... la
mia soluzione è:

1 / addOnPageChangeListener per l'adattatore:

mViewPager.addOnPageChangeListener(this);

2 / implementare OnPageChangeListener:

public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener

3 / sovrascrivi i 3 metodi:

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

}

@Override
public void onPageSelected(int position)
{ 
}

@Override
public void onPageScrollStateChanged(int state)
{
}

4 / dichiara e inizializza questa variabile sulla tua classe

private static int mTabState = 1;

avviso : ho tre frammenti nel mio adattatore e uso mTabState per setCurrentItem e la posizione corrente dell'adattatore che riconoscono quale frammento viene mostrato all'utente nel tempo ... 5 / nel metodo onPageSelected aggiungi questi codici:

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

se la pagina precedente o la pagina corrente è la pagina 0 (frammento in posizione 0) allora fai questa roba

6 / ora nella tua classe di frammento (frammento nella posizione 0 dell'adattatore), devi creare il ricevitore di trasmissione e registrarlo nel metodo onResume e annullarne la registrazione suPause methos:

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);
}

riepilogo: ho un adattatore per cercapersone per frammenti che mostra tre frammenti, voglio mostrare alcune animazioni sulle viste in frammento nella posizione 0 dell'adattatore, per questo utilizzo BroadcastReceiver. Quando il frammento viene selezionato, avvio il metodo di animazione e mostra le visualizzazioni all'utente, quando il frammento non viene mostrato all'utente provo a visualizzazioni invisibili ...


1

Prova questo codice per risolvere il problema dell'aggiornamento della visualizzazione in Viewpager ....

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

0

per la funzione "instantiateItem", prepara semplicemente il frammento, ma non caricare il contenuto pesante.

Usa "onPageChangeListener", in modo che ogni volta che vai su una pagina specifica, carichi il suo contenuto pesante e lo mostri.


0

Ho lo stesso problema. Ho trovato del codice utile su questo sito e l'ho trasformato.

Il minimo int per mViewPager.setOffscreenPageLimit (...); è 1, quindi anche se lo cambi in 0 avrai ancora 2 pagine caricate.

La prima cosa da fare è creare un int statico che chiameremo maxPageCount e sovrascriveremo il metodo FragmentStatePagerAdapter getCount () per restituire maxPageCount:

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

Crea quindi un metodo statico accessibile da qualsiasi punto del programma che ti permetta di modificare questo maxCount:

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

Ora inizializza maxPageCount su 1. Quando vuoi puoi aggiungere un'altra pagina. Nel mio caso, quando avevo bisogno che l'utente trattasse la pagina corrente prima di generare l'altra. Lo fa e poi, senza problemi, può passare alla pagina successiva.

Spero che aiuti qualcuno.


0

Usa questo // crea booleano per il recupero dei dati booleano privato 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;
    }
} 
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.