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