ViewPager.setOffscreenPageLimit (0) nie działa zgodnie z oczekiwaniami

100

Fragmenty, których używam w mojej ViewPagerinstancji, są dość zasobochłonne, więc chciałbym ładować tylko jeden naraz. Kiedy próbuję wykonać następujące czynności:

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

Moja FragmentStatePagerAdapter.getItem(int position)funkcja nadpisywania jest wywoływana 3 razy, co się dzieje, gdy dzwonię mViewPager.setOffscreenPageLimit(1). Spodziewałbym się, że zostanie wywołany tylko raz, ponieważ podałem 0 stron pozaekranowych.

Wydaje mi się, że wszystko dzwonię poprawnie, bo jak dzwonię mViewPager.setOffscreenPageLimit(2), dzwoni FragmentStatePagerAdapter.getItem(int position)5 razy, jak bym się spodziewał.

Czy ViewPager wymaga co najmniej 1 strony poza ekranem, czy też robię coś nie tak?

Chris Lacy
źródło
9
Dodałem prośbę o funkcję: code.google.com/p/android/issues/detail?id=56667
Zaznacz
Miałem tylko 3 zakładki i ustawienie ViewPager.setOffscreenPageLimit(2)działało dla mnie. SpróbujViewPager.setOffscreenPageLimit(totTabs - 1)
sibin

Odpowiedzi:

112

Czy ViewPager wymaga co najmniej 1 strony poza ekranem

Tak. Jeśli dobrze czytam kod źródłowy, powinieneś otrzymać ostrzeżenie o tym w LogCacie, coś takiego:

Requested offscreen page limit 0 too small; defaulting to 1
CommonsWare
źródło
6
To nie jest rozwiązanie! Chcę załadować tylko 1 fragment, a nie 2. Na pewno jest to jakoś możliwe?
HGPB,
14
@Haraldo: "Z pewnością jest to jakoś możliwe?" -- nie zViewPager . ViewPagertworzy te fragmenty, aby widoki istniały, aby użytkownik mógł przesuwać się między nimi, z animowanym efektem pokazującym zsuwanie się starego widoku z ekranu i przesuwanie nowego widoku na ekran. Możesz spróbować napisać własną, ViewPagerktóra może przesuwać się między rzeczami, które nie istnieją. Możesz przeczytać więcej na ten temat na code.google.com/p/android/issues/detail?id=56667#c3
CommonsWare,
2
Tak, przeczytałem ten numer. Po prostu nie jest logiczne. Spodziewam się, że będę w stanie osiągnąć setOffscreenPageLimit(0)0. Wtedy sam poradzę sobie z konsekwencjami. Domyślny pusty fragment może być na przykład symbolem zastępczym do czasu załadowania fragmentu. Wszystko to może mieć miejsce poza wątkiem interfejsu użytkownika. Zresztą nie myślałem o tym dużo.
HGPB,
5
@Haraldo: "Domyślny pusty fragment może być symbolem zastępczym na przykład do czasu załadowania fragmentu" - możesz mieć dziś element zastępczy w swoim fragmencie, z istniejącą implementacją ViewPager, taki, który zastępujesz rzeczywistą treścią, gdy jest dostępny .
CommonsWare,
2
onPageChangeListener wydaje się rozwiązaniem.
alicanbatur
123

Najlepszy sposób, jaki znalazłem, to setUserVisibleHint
dodaj to do swojego fragmentu

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}
Tyler Davis
źródło
1
+++++ za to rozwiązanie !!! dzięki, po dodaniu tego kodu otrzymałem NPL po raz pierwszy, właśnie dodałem blok try catch i działa idealnie dla mnie !!!
Bhavin Chauhan
1
Używając tego, nie ładuje daty na SecondViewPager Fragment, na drugim fragmencie ładuje dane trzeciego fragmentu. Czy ktoś może mi pomóc?
Arshad,
Czy czegoś mi brakuje, wywołano setUserVisibleHint przed onCreateView
Anton Makov
1
Najlepsze rozwiązanie jest teraz przestarzałe
M.Sameer,
@ M.Sameer jakaś alternatywa dla setUserVisibleHint ma deprectate?
Yudi karma
8

Możesz spróbować tak:

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
źródło
najlepsza odpowiedź!
Radesh
7

Najpierw dodaj

   boolean isFragmentLoaded = false;

niż

@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.
źródło
7
ale ta metoda wymaga przed onCreateView
AndroidGeek
2

ViewPager domyślnie ładuje następną stronę (fragment), której nie można zmienić za pomocą setOffscreenPageLimit (0). Ale możesz coś zrobić, żeby się włamać. Funkcję onPageSelected można zaimplementować w działaniu zawierającym ViewPager. W następnym fragmencie (którego nie chcesz ładować), piszesz funkcję, powiedzmy showViewContent (), w której umieszczasz cały kod init pochłaniający zasoby i nie robisz nic przed metodą onResume (). Następnie wywołaj funkcję showViewContent () wewnątrz onPageSelected. Mam nadzieję, że to pomoże.

Friends_little_black
źródło
1

może to być stary wątek, ale wydaje mi się, że działa. Zastąp tę funkcję:

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

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

szczęśliwe kodowanie ...

ralphgabb
źródło
co w przeglądarce internetowej? Proponuję stworzyć w tym celu nowy wątek.
ralphgabb
1

w moim przypadku chciałem uruchomić animacje w widokach, ale z setUserVisibleHint mam kilka problemów ...
moje rozwiązanie to:

1 / addOnPageChangeListener dla twojego adaptera:

mViewPager.addOnPageChangeListener(this);

2 / Zaimplementuj OnPageChangeListener:

public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener

3 / zastąpić 3 metody:

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

}

@Override
public void onPageSelected(int position)
{ 
}

@Override
public void onPageScrollStateChanged(int state)
{
}

4 / zadeklaruj i zainicjalizuj tę zmienną w swojej klasie

private static int mTabState = 1;

uwaga : mam trzy fragmenty w moim adapterze i używam mTabState dla setCurrentItem i aktualnej pozycji adaptera, które rozpoznają, który fragment jest wyświetlany użytkownikowi w czasie ... 5 / w metodzie onPageSelected dodaj następujące kody:

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

jeśli poprzednia lub bieżąca strona to strona 0 (fragment na pozycji 0), zrób to

6 / teraz w swojej klasie fragmentów (fragment na pozycji 0 adaptera) należy stworzyć odbiornik rozgłoszeniowy i zarejestrować go w metodzie onResume oraz wyrejestrować go metody onPause:

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

podsumowanie: mam Fragment Pager Adapter, który pokazuje w nim trzy fragmenty, chcę pokazać animacje na widokach w fragmencie w pozycji 0 adaptera, do tego używam BroadcastReceiver. Po wybraniu fragmentu uruchamiam metodę Animation i pokazuję widoki użytkownikowi, gdy fragment nie jest wyświetlany użytkownikowi, próbuję wyświetlić niewidoczne widoki ...

AREF
źródło
1

Wypróbuj ten kod, aby rozwiązać problem z odświeżaniem widoku w 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
źródło
0

w przypadku funkcji „instantiateItem” po prostu przygotuj fragment, ale nie ładuj ciężkiej zawartości.

Użyj "onPageChangeListener", aby za każdym razem, gdy przechodzisz do określonej strony, ładowałeś jej ciężką zawartość i wyświetlał ją.

programista Androida
źródło
0

Mam taki sam problem. Znalazłem przydatny kod na tej stronie i przekształciłem go.

Minimalna wartość int dla mViewPager.setOffscreenPageLimit (...); wynosi 1, więc nawet jeśli zmienisz ją na 0, nadal będziesz mieć załadowane 2 strony.

Pierwszą rzeczą do zrobienia jest utworzenie statycznego int, który wywołujemy maxPageCount i nadpisujemy metodę FragmentStatePagerAdapter getCount (), aby zwrócić wartość maxPageCount:

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

Następnie utwórz statyczną metodę dostępną z dowolnego miejsca w programie, która pozwoli Ci zmienić ten maxCount:

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

Teraz zainicjuj maxPageCount na 1. Kiedy chcesz, możesz dodać kolejną stronę. W moim przypadku, gdy potrzebowałem, aby użytkownik najpierw potraktował bieżącą stronę, zanim wygenerował drugą. Robi to, a potem bez problemu może przejść na następną stronę.

Mam nadzieję, że to komuś pomoże.

K20
źródło
0

Use This // create boolean do pobierania danych private boolean 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
źródło