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

13 ответов

Решение

Требуется ли 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
    }
}

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

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

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

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

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

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

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

счастливого кодирования...

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

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

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

Просмотр пейджера только с одной страницей:

Это 2021 год - февраль, Альхамдулиллах, я могу добавить только одну страницу с помощью viewPager. Подход заключается в следующем: ViewPager, FragmentPagerAdapter, Tablayout, а fragment. В моем случае я могу заполнить много Pages со многими tabs, или только один Page с одним Tab. Когда одна вкладка и одна страница смахивают влево или вправо, мне удается изменить главу моего документа (которую я хочу показать дальше). А когда много страниц и много вкладок, я могу изменить всю книгу документов.

В основном Activity Oncreate: (Вот мой подход к рабочему коду, я здесь ничего не изменил из моего рабочего кода):

      if(getIntent()!=null){
            if(getIntent().getStringExtra("ONLY_TAFHEEM")!=null)
             sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager(), suraName, suraId, ayatId, true);
            else
            sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager(), suraName, suraId, ayatId, false);
        }else {
            sectionsPagerAdapter = new SectionsPagerAdapter(this, getSupportFragmentManager(), suraName, suraId, ayatId, false);
        }

        final ViewPager viewPager = findViewById(R.id.view_pager);
        viewPager.setAdapter(sectionsPagerAdapter);
        tabsLayout = findViewById(R.id.tabs);
        tabsLayout.animate();

        tabsLayout.setupWithViewPager(viewPager);

В адаптере:

       @NonNull
    @Override
    public Fragment getItem(int position) {

          
    // getItem is called to instantiate the fragment for the given page.
    // Return a PlaceholderFragment (defined as a static inner class below).
    return PlaceholderFragment.sendData(mContext, postion, suraName, suraId, ayahId, ARABIC_AYAH, BENGALI_AYAH, actualDbNames[position], tafsirDisplayNames[position]);
}


@Nullable
@Override
public CharSequence getPageTitle(int position) {
   
    return tafsirDisplayNames[position];
}

@Override
public int getCount() {
   // this is the tricky part // Show pages according to array length. // this may only one // this is the tricky part : 
    return tafsirDisplayNames.length;
}

И, наконец, публичный конструктор фрагментов:

       public static PlaceholderFragment sendData(Context mContext, int tabIndex, String suraName, String suraId, String ayahNumber, String arabicAyah, String bengaliAyah, String actualDbName, String displayDbName) {

        Log.i("PlaceHolder", "Tafhim sendData: " + bengaliAyah);
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle bundle = new Bundle();
        mContext_ = mContext;
        BENGALI_AYAH = bengaliAyah;
        _DISPLAY_DB_NAME = displayDbName;
        bundle.putInt(ARG_SECTION_NUMBER, tabIndex);
        bundle.putString(SURA_NAME, suraName);
        bundle.putString(SURA_ID, suraId);
        bundle.putString(AYAH_NUMBER, ayahNumber);
        bundle.putString(ARABIC_AYAH, arabicAyah);
        bundle.putString(ACTUAL_DB_NAME, actualDbName);
        bundle.putString(DISPLAY_DB_NAME, displayDbName);
        fragment.setArguments(bundle);
        return fragment;
    }

Вот и все, просто передавая массив (ярлыков вкладок) адаптеру (это может быть только один элемент, в случае, для одной страницы), с моей потребностью я могу заполнить одну страницу или несколько страниц, и в соответствии с этим он заполнит одна вкладка или несколько вкладок: в приведенном выше коде массив: tafsirDisplayNames. Я также могу создать массив вручную в адаптере при первом вызове адаптера или воссоздать массив с + -элементами при воссоздании MainActivity.

Thats, All, Alhamdulillah, Удачного кодирования ........

Пожалуйста, попробуйте этот код для решения проблемы обновления представления в Viewpager....

/* DO NOT FORGET! The ViewPager requires at least “1” minimum OffscreenPageLimit */
int limit = (mAdapter.getCount() > 1 ? mAdapter.getCount() - 1 : 1);
mViewPager.setOffscreenPageLimit(limit);

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

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

Используйте это // создайте логическое значение для извлечения данных 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;
    }
} 
      // step 1: add  BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT FragmentPagerAdapter contractor
   
    public class BottomTabViewPager extends FragmentPagerAdapter {
    
        private final List<Fragment> mFragmentList = new ArrayList<>();
        private final List<String> mFragmentTitleList = new ArrayList<>();
    
        public BottomTabViewPager(FragmentManager manager) {
            super(manager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
    
        }
    
        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position);
        }
    
        @Override
        public int getCount() {
            return mFragmentList.size();
        }
    
        public void addFragment(Fragment fragment, String title) {
            mFragmentList.add(fragment);
            mFragmentTitleList.add(title);
        }
    
        public void addTabs(String title) {
    
            mFragmentTitleList.add(title);
        }
    
    
        @Override
        public CharSequence getPageTitle(int position) {
            return mFragmentTitleList.get(position);
            // return null;
        }
    }
    
    
   // step  2:  You can try like this :

    
    public class MyFragment extends Fragment  {
    
       
    
        public MyFragment() {
            // Required empty public constructor
        }
    
    
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                                 Bundle savedInstanceState) {
            // Inflate the layout for this fragment
            View view = inflater.inflate(R.layout.fragment_ui, container, false);
            
            return view;
        }
    
     @Override
        public void onResume() {
            super.onResume();
            /**
             * Load your stuffs here.
             */
        }
    
      }

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

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

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

Другие вопросы по тегам