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

100

Фрагменты, которые я использую в своем 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 закадровая страница, или я здесь что-то делаю не так?

Крис Лейси
источник
9
Я добавил запрос функции: code.google.com/p/android/issues/detail?id=56667
Марк,
stackoverflow.com/a/44405015/7874047
Ронак Таккар 07
У меня было всего 3 вкладки и настройка ViewPager.setOffscreenPageLimit(2)у меня работала. ПопробуйтеViewPager.setOffscreenPageLimit(totTabs - 1)
Sibin

Ответы:

112

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

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

Requested offscreen page limit 0 too small; defaulting to 1
CommonsWare
источник
6
Это не выход! Я хочу загрузить только 1 фрагмент, а не 2. Неужели это как-то возможно?
HGPB
14
@Haraldo: "Конечно, это возможно?" - не с ViewPager. ViewPagerсоздает эти фрагменты, чтобы представления существовали, чтобы пользователь мог перемещаться между ними, с анимированным эффектом, показывающим, что старый вид скользит с экрана, а новый вид скользит по экрану. Вы можете попробовать написать свой собственный, ViewPagerкоторый может пролистывать несуществующие вещи. Вы можете узнать больше об этом на code.google.com/p/android/issues/detail?id=56667#c3
CommonsWare
2
Да, я прочитал этот выпуск. Это просто не логично. Я ожидаю, что смогу setOffscreenPageLimit(0)получить 0. Тогда я сам смогу справиться с последствиями. Пустой фрагмент по умолчанию может быть заполнителем, например, до тех пор, пока не загрузится один фрагмент. Все это может происходить вне потока пользовательского интерфейса. Во всяком случае, я об этом особо не думал.
HGPB
5
@Haraldo: «Пустой фрагмент по умолчанию может быть заполнителем, например, до тех пор, пока один из фрагментов не загрузится» - вы можете иметь заполнитель в своем фрагменте сегодня, с существующей реализацией ViewPager, который вы заменяете реальным содержимым, когда он доступен .
CommonsWare
2
onPageChangeListener кажется решением.
alicanbatur
123

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

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}
Тайлер Дэвис
источник
1
+++++ за это решение !!! спасибо, после добавления этого кода я впервые получил NPL, я просто добавил блок try catch, и он отлично работает для меня !!!
Бхавин Чаухан
1
При использовании этого он не загружает дату во втором фрагменте ViewPager, во втором фрагменте данные загрузки третьего фрагмента. Кто-нибудь может мне помочь?
Аршад
Я что-то упускаю, setUserVisibleHint вызывал раньше onCreateView
Антон Маков
1
Лучшее решение устарело
M.Sameer
@ M.Sameer есть ли альтернатива setUserVisibleHint?
Юди карма
8

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

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(){}
Терри
источник
лучший ответ!
Radesh
7

Первое добавление

   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{
     }
}
Арпит Дж.
источник
7
но этот метод вызывает до onCreateView
AndroidGeek,
2

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

Friends_little_black
источник
1

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

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

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

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

ralphgabb
источник
что на веб-просмотре? Я предлагаю вам создать для этого новую ветку.
ralphgabb
1

в моем случае я хотел запустить некоторые анимации в представлениях, но с 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:

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

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

AREF
источник
1

Попробуйте этот код, чтобы решить проблему с обновлением представления в 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

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

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

разработчик Android
источник
0

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

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

Первое, что нужно сделать, это создать статический int, который мы вызовем maxPageCount и переопределим метод getCount () FragmentStatePagerAdapter, чтобы вернуть 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. Когда захотите, вы можете добавить еще одну страницу. В моем случае, когда мне нужно, чтобы пользователь сначала обработал текущую страницу, прежде чем сгенерировать другую. Он это делает, а затем без проблем может перелистнуть на следующую страницу.

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

K20
источник
0

Use This // создать логическое значение для выборки данных 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;
    }
} 
Рохит Шарма
источник