ViewPager.setOffscreenPageLimit(0) не работает должным образом


фрагменты, которые я использую в моем ViewPager экземпляр довольно ресурсоемкий, поэтому я хотел бы загружать только по одному за раз. Когда я пытаюсь следующее:

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

мой FragmentStatePagerAdapter.getItem(int position) функция переопределения вызывается 3 раза, что и происходит, когда я вызываю mViewPager.setOffscreenPageLimit(1). Я ожидал бы, что он будет вызван только один раз, потому что я указал 0 закадровых страниц.

Я считаю, что я называю все правильно, потому что если я позвоню mViewPager.setOffscreenPageLimit(2),FragmentStatePagerAdapter.getItem(int position) называется 5 раз, как я и ожидал.

требуется ли ViewPager минимум 1 закадровых страниц, или я делаю что-то неправильно здесь?

10 87

10 ответов:

требуется ли ViewPager как минимум 1 закадровых страниц

да. Если я читаю исходный код правильно, вы должны получать предупреждение об этом в LogCat, что-то вроде:

Requested offscreen page limit 0 too small; defaulting to 1

лучший способ, который я нашел был setUserVisibleHint
добавьте это в свой фрагмент

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

Добавить

   boolean isFragmentLoaded = false;

чем

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

        isFragmentLoaded = true;
    }
else{
     }
}

вы можете попробовать вот так :

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

ViewPager по умолчанию загружает следующую страницу (фрагмент), которую вы не можете изменить с помощью setOffscreenPageLimit(0). Но вы можете сделать что-то взломать. Вы можете реализовать функцию onPageSelected в действии, содержащем ViewPager. В следующем фрагменте (который вы не хотите загружать) вы пишете функцию, скажем, showViewContent (), где вы помещаете весь ресурсоемкий init-код и ничего не делаете перед методом onResume (). Затем вызовите функцию showViewContent () внутри onPageSelected. Надеюсь, что это будет помощь.

для функции "instantiateItem" просто подготовьте фрагмент, но не загружайте тяжелый контент.

используйте "onPageChangeListener", так что каждый раз, когда вы идете на определенную страницу, вы загружаете ее тяжелый контент и показываете его.

это может быть старый нить, но это, кажется, работает для меня. Переопределите эту функцию:

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

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

счастливый кодировок...

у меня такая же проблема. Я нашел полезный код на этом сайте и преобразовал его.

min int для mViewPager.setOffscreenPageLimit(...); равно 1, поэтому даже если вы измените его на 0, у вас все равно будет загружено 2 страницы.

Первое, что нужно сделать, это создать статический int мы вызовем maxPageCount и переопределим метод FragmentStatePagerAdapter getCount () для возврата maxPageCount:

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

затем создайте статический метод, доступный из любого места в программа, которая позволит вам изменить этот maxCount:

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

теперь инициализируйте maxPageCount до 1. Когда вы хотите, вы можете добавить еще одну страницу. В моем случае, когда мне нужно было, чтобы пользователь сначала обработал текущую страницу, прежде чем сгенерировать другую. Он это делает и потом, без проблем может пролистать до следующей страницы.

надеюсь, что это поможет кому-то.

Использовать Этот // создать логическое значение для выборки данных 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;
    }
} 

в моем случае я хотел, чтобы начать некоторые анимации в представлениях, но с setUserVisibleHint получил некоторые проблемы ...
мое решение:

1/ addOnPageChangeListener для вашего адаптера :

mViewPager.addOnPageChangeListener(this);

2 / реализовать OnPageChangeListener:

public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener

3 / переопределите 3 метода:

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

}

@Override
public void onPageSelected(int position)
{ 
}

@Override
public void onPageScrollStateChanged(int state)
{
}

4 / объявите и инициализируйте эту переменную в своем классе

private static int mTabState = 1;

обратите внимание : у меня есть три фрагмента в моем адаптере, и используйте mTabState для setCurrentItem и текущего положения адаптера, которые распознают, какой фрагмент отображается пользователю во времени ... 5 / в методе onPageSelected добавьте следующие коды:

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

если предыдущая страница или текущая страница-это страница 0 (фрагмент в позиции 0), то сделайте это

6 / теперь в вашем классе фрагмента (фрагмент в позиции 0 адаптера) вы должны создать широковещательный приемник и зарегистрировать его в методе onResume и отменить его регистрацию onPause 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);
}

резюме : у меня есть фрагмент пейджера адаптер ведьма показывает три фрагмента в нем, я хочу показать некоторые анимации на представлениях в фрагменте в позиции 0 адаптера, для этого я использую BroadcastReceiver. Когда фрагмент выбран, я запускаю метод анимации и показываю представления пользователю, когда фрагмент не отображается пользователю, я пытаюсь невидимые представления...