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?




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

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

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

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

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.


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 ...


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 ...


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

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ą.


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.


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;
    }
} 
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.