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/isissue/detail?id=56667
Марк


У мене було лише 3 вкладки і настройка ViewPager.setOffscreenPageLimit(2)працювала на мене. СпробуйтеViewPager.setOffscreenPageLimit(totTabs - 1)
sibin

Відповіді:


112

Чи потрібен ViewPager мінімум 1 сторінки на екрані

Так. Якщо я читаю вихідний код правильно, вам слід отримувати попередження про це в LogCat, приблизно так:

Requested offscreen page limit 0 too small; defaulting to 1

6
Це не рішення! Я хочу завантажити лише 1 фрагмент, а не 2. Невже це можливо якось?
HGPB

14
@Haraldo: "Звичайно, це можливо якось?" - не з ViewPager. ViewPagerстворює ці фрагменти, щоб перегляди існували, тож користувач може переходити між ними, причому анімований ефект показує, як старий вид ковзає з екрана, а новий вигляд ковзає на екран. Ви можете спробувати написати своє, ViewPagerщо може проходити між речами, які не існують. Більше про це можна прочитати на code.google.com/p/android/isissue/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 вперше, я просто додав спробувати блок лову, і він працює ідеально для мене !!!
Бхавін Чаухан

1
Використовуючи це, він не завантажує дату на secondViewPager Fragment, на другому фрагменті дані його завантаження третього фрагмента. Може хтось допоможе мені?
Аршад

Я щось пропускаю, setUserVisibleHint зателефонував перед onCreateView
Антон Маков

Зараз найкраще рішення застаріло
М.Самер

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

що найкраща відповідь!
Радеш

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

6
але цей метод закликає до
onCreateView

2

ViewPager за замовчуванням завантажує наступну сторінку (фрагмент), яку ви не можете змінити setOffscreenPageLimit (0). Але ви можете щось зламати. Ви можете реалізувати функцію onPageSelected в Активність, що містить ViewPager. У наступному фрагменті (який ви не хочете завантажувати) ви пишете функцію, скажімо, showViewContent (), де ви вводите весь ресурс, що споживає код init, і нічого не робити перед методом onResume (). Потім викличте функцію showViewContent () всередині onPageSelected. Сподіваюсь, це допоможе.


1

це може бути стара тема, але це, здається, працює для мене. Замініть цю функцію:

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

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

щасливі кодування ...


Що на веб-перегляді? Я пропоную вам створити для цього нову нитку.
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 та скасувати реєстрацію на методіПауза:

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. Коли фрагмент вибрано, я запускаю метод анімації та показує «Перегляди користувачеві», коли «Фрагмент не відображається користувачеві», я намагаюся «Невидимі види»


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", щоб кожен раз, коли ви переходили на певну сторінку, ви завантажували її важкий вміст і показували його.


0

У мене наче одна і та ж проблема. Я знайшов на цьому сайті якийсь корисний код і перетворив його.

Мінімум 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. Коли ви хочете, можете додати іншу сторінку. У моєму випадку, коли мені знадобився користувач, щоб обробити поточну сторінку першою, перш ніж створити другу. Він це зробить і тоді без проблем може перейти на наступну сторінку.

Сподіваюся, це допоможе комусь.


0

Використовуйте це // створення булевих даних для отримання приватних булевих даних 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;
    }
} 
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.