ViewPager.setOffscreenPageLimit (0) funktioniert nicht wie erwartet

100

Die Fragmente, die ich in meiner ViewPagerInstanz verwende, sind sehr ressourcenintensiv, daher möchte ich jeweils nur eines laden. Wenn ich Folgendes versuche:

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

Meine FragmentStatePagerAdapter.getItem(int position)Override-Funktion wird dreimal aufgerufen, was passiert, wenn ich anrufe mViewPager.setOffscreenPageLimit(1). Ich würde erwarten, dass es nur einmal aufgerufen wird, da ich 0 Offscreen-Seiten angegeben habe.

Ich glaube, ich rufe alles richtig an, denn wenn ich anrufe mViewPager.setOffscreenPageLimit(2), FragmentStatePagerAdapter.getItem(int position)wird es 5 Mal angerufen, wie ich es erwarten würde.

Benötigt ViewPager mindestens 1 Offscreen-Seiten oder mache ich hier etwas falsch?

Chris Lacy
quelle
9
Ich habe eine Feature - Anfrage hinzugefügt: code.google.com/p/android/issues/detail?id=56667
Mark
Ich hatte nur 3 Registerkarten und die Einstellung ViewPager.setOffscreenPageLimit(2)funktionierte für mich. TryViewPager.setOffscreenPageLimit(totTabs - 1)
sibin

Antworten:

112

Benötigt ViewPager mindestens 1 Offscreen-Seiten?

Ja. Wenn ich den Quellcode richtig lese, sollten Sie in LogCat eine Warnung erhalten, etwa:

Requested offscreen page limit 0 too small; defaulting to 1
CommonsWare
quelle
6
Dies ist keine Lösung! Ich möchte nur 1 Fragment laden, nicht 2. Sicher ist das irgendwie möglich?
HGPB
14
@ Haraldo: "Sicher ist das irgendwie möglich?" - nicht mit ViewPager. ViewPagerErstellt diese Fragmente so, dass die Ansichten vorhanden sind, sodass der Benutzer zwischen ihnen wischen kann. Der animierte Effekt zeigt, dass die alte Ansicht vom Bildschirm gleitet und die neue Ansicht auf den Bildschirm gleitet. Sie können gerne versuchen, Ihre eigenen zu schreiben ViewPager, die zwischen Dingen wechseln können, die es nicht gibt. Sie können mehr darüber unter code.google.com/p/android/issues/detail?id=56667#c3
CommonsWare
2
Ja, ich habe diese Ausgabe gelesen. Ist einfach nicht logisch. Ich erwarte, in der Lage setOffscreenPageLimit(0)zu sein 0. Dann kann ich die Konsequenzen selbst behandeln. Ein leeres Standardfragment kann beispielsweise ein Platzhalter sein, bis ein Fragment geladen wurde. All dies könnte außerhalb des UI-Threads stattfinden. Jedenfalls habe ich nicht viel darüber nachgedacht.
HGPB
5
@Haraldo: "Ein leeres Standardfragment kann beispielsweise ein Platzhalter sein, bis ein Fragment geladen wurde" - Sie können heute gerne einen Platzhalter in Ihrem Fragment haben, der bereits vorhanden ist ViewPagerund den Sie durch den tatsächlichen Inhalt ersetzen, sofern verfügbar .
CommonsWare
2
onPageChangeListener scheint die Lösung zu sein.
Alicanbatur
123

Der beste Weg, den ich gefunden habe, war setUserVisibleHint. Fügen
Sie dies Ihrem Fragment hinzu

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}
Tyler Davis
quelle
1
+++++ für diese Lösung !!! danke, nachdem ich diesen Code hinzugefügt habe, habe ich NPL zum ersten Mal bekommen, ich habe gerade try catch block hinzugefügt und es funktioniert perfekt für mich !!!
Bhavin Chauhan
1
Wenn Sie dies verwenden, wird das Datum nicht in das zweite ViewPager-Fragment geladen, sondern in das zweite Fragment werden die Ladedaten des dritten Fragments geladen. Kann mir jemand helfen?
Arshad
Vermisse ich etwas, das setUserVisibleHint vor onCreateView aufgerufen hat
Anton Makov
Die beste Lösung ist jetzt veraltet
M.Sameer
@ M.Sameer hat eine Alternative für setUserVisibleHint korrekt?
Yudi Karma
8

Sie können Folgendes versuchen:

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
quelle
diese beste Antwort!
Radesh
7

Zuerst hinzufügen

   boolean isFragmentLoaded = false;

als

@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.
quelle
6
aber diese Methode ruft vor onCreateView
Zar E Ahmer
2

ViewPager lädt standardmäßig die nächste Seite (Fragment), die Sie nicht mit setOffscreenPageLimit (0) ändern können. Aber Sie können etwas tun, um zu hacken. Sie können die Funktion onPageSelected in Aktivität implementieren, die den ViewPager enthält. Im nächsten Fragment (das Sie nicht laden möchten) schreiben Sie eine Funktion, beispielsweise showViewContent (), in der Sie den gesamten ressourcenintensiven Init-Code eingeben und vor der onResume () -Methode nichts tun. Rufen Sie dann die Funktion showViewContent () in onPageSelected auf. Hoffe das wird helfen.

Friends_little_black
quelle
1

Das mag ein alter Thread sein, aber das scheint für mich zu funktionieren. Überschreiben Sie diese Funktion:

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

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

fröhliche Codierungen ...

ralphgabb
quelle
Was ist auf Webview? Ich schlage vor, dass Sie dafür einen neuen Thread erstellen.
Ralphphgabb
1

In meinem Fall wollte ich einige Animationen in Ansichten starten, aber mit setUserVisibleHint gab es einige Probleme ...
meine Lösung ist:

1 / addOnPageChangeListener für Ihren Adapter:

mViewPager.addOnPageChangeListener(this);

2 / OnPageChangeListener implementieren:

public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener

3 / überschreibe die 3 Methoden:

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

}

@Override
public void onPageSelected(int position)
{ 
}

@Override
public void onPageScrollStateChanged(int state)
{
}

4 / deklariere und initialisiere diese Variable in deiner Klasse

private static int mTabState = 1;

Hinweis : Ich habe drei Fragmente in meinem Adapter und verwende mTabState für setCurrentItem und die aktuelle Position des Adapters, die erkennen, welches Fragment dem Benutzer rechtzeitig angezeigt wird ... 5 / in der onPageSelected-Methode fügen Sie diese Codes hinzu:

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

Wenn die vorherige oder aktuelle Seite Seite 0 ist (Fragment an Position 0), dann machen Sie dieses Zeug

6 / Jetzt müssen Sie in Ihrer Fragmentklasse (Fragment an Position 0 des Adapters) einen Rundfunkempfänger erstellen, ihn in der onResume-Methode registrieren und die Registrierung bei onPause methos aufheben:

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

Zusammenfassung: Ich habe einen Fragment-Pager-Adapter, der drei Fragmente enthält. Ich möchte einige Animationen in Ansichten in Fragment in Position 0 des Adapters anzeigen. Dafür verwende ich BroadcastReceiver. Wenn Fragment ausgewählt ist, starte ich die Animationsmethode und zeige dem Benutzer die Ansichten. Wenn Fragment dem Benutzer nicht angezeigt wird, versuche ich, unsichtbare Ansichten anzuzeigen.

AREF
quelle
1

Bitte versuchen Sie diesen Code, um das Problem der Aktualisierung der Ansicht in Viewpager zu beheben.

/* 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
quelle
0

Bereiten Sie für die Funktion "instantiateItem" nur das Fragment vor, laden Sie jedoch nicht den umfangreichen Inhalt.

Verwenden Sie "onPageChangeListener", damit Sie jedes Mal, wenn Sie zu einer bestimmten Seite gehen, deren umfangreichen Inhalt laden und anzeigen.

Android-Entwickler
quelle
0

Ich habe irgendwie das gleiche Problem. Ich habe auf dieser Site nützlichen Code gefunden und ihn transformiert.

Das min int für mViewPager.setOffscreenPageLimit (...); ist 1, und selbst wenn Sie es auf 0 ändern, werden immer noch 2 Seiten geladen.

Als erstes müssen Sie ein statisches int erstellen. Wir rufen maxPageCount auf und überschreiben die FragmentStatePagerAdapter-Methode getCount (), um maxPageCount zurückzugeben:

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

Erstellen Sie dann eine statische Methode, auf die von einer beliebigen Stelle im Programm aus zugegriffen werden kann, mit der Sie diesen maxCount ändern können:

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

Initialisieren Sie nun maxPageCount auf 1. Wann immer Sie möchten, können Sie eine weitere Seite hinzufügen. In meinem Fall, als der Benutzer zuerst die aktuelle Seite behandeln musste, bevor die andere generiert wurde. Er macht es und kann dann problemlos zur nächsten Seite wischen.

Hoffe es hilft jemandem.

K20
quelle
0

Verwenden Sie diesen // Booleschen Wert zum Abrufen von Daten. Privater Boolescher Wert 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
quelle