44

I understand the lowest number I can give setOffscreenPageLimit(int) is 1. but I need to load one page at a time because memory problems.

Am i going to have to use the old style tabhost etc? or is there a way/hack I can make my viewPager load one page at a time?

My Adapter extends BaseAdapter with the ViewHolder patern.

Ronak Thakkar
  • 2,515
  • 6
  • 31
  • 45
124697
  • 22,097
  • 68
  • 188
  • 315
  • see the link [http://stackoverflow.com/questions/17948681/viewpager-setoffscreenpagelimit0-has-no-effect-at-all-how-do-i-only-load-one/31531724#31531724][1] [1]: http://stackoverflow.com/questions/17948681/viewpager-setoffscreenpagelimit0-has-no-effect-at-all-how-do-i-only-load-one/31531724#31531724 – Yuhan Zhang Jul 21 '15 at 06:37
  • Check this [answer](https://stackoverflow.com/a/56056285/7914153) . It solves the problem of me. – Gopinath May 09 '19 at 09:31

9 Answers9

42

I was having the same problem and I found the solution for it:

Steps:

1) First Download the CustomViewPager Class from this link.

2) Use that class as mentioned below:

In Java:

CustomViewPager mViewPager;
mViewPager = (CustomViewPager) findViewById(R.id.swipePager);
mViewPager.setOffscreenPageLimit(0);

In XML:

<com.yourpackagename.CustomViewPager 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/swipePager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>

Now only one page will be loaded at once.

P.S: As per the question's requirement, I have posted the solution for Viewpager. I haven't tried the same with TabLayout yet. If I will find any solution for that I will update the answer.

In this file, KeyEventCompat is used it may not be found by the Android studio because KeyEnentCompat class was deprecated in API level 26.0.0 so you need to replace KeyEventCompat with event for more details you can view https://developer.android.com/sdk/support_api_diff/26.0.0-alpha1/changes/android.support.v4.view.KeyEventCompat

Ronak Thakkar
  • 2,515
  • 6
  • 31
  • 45
  • 5
    Not able to setup with TabLayout, Please advise – RaRa Jul 07 '17 at 09:32
  • 2
    @RRR_1173, Use any general example for viewpager and just replace your Viewpager to CustomViewPager. you can find the general example here: http://www.androidhive.info/2013/10/android-tab-layout-with-swipeable-views-1/ You just need to give the CustomClass. – Ronak Thakkar Jul 07 '17 at 12:35
  • 3
    tabLayout.setupWithViewPager(customeViewPager); here Tablayout not accepting the Custom class. – RaRa Jul 10 '17 at 12:02
  • 2
    Not able to setup with TabLayout, Please advise – Shrikant Jan 11 '18 at 13:12
  • Working fine. Thank you very much. But If use pager indicator, that position not changing. – ranjith Jan 18 '18 at 08:55
  • Works fine. Thank You very much. @ranjith for changing the position u need to change the OnPageChangeListener to TabLayout.TabLayoutOnPageChangeListener and also add viewPager.setOnPageChangeListener(new TabLayout.TabLayoutOnPageChangeListener(tabLayout){}); to the viewpager – Prateek218 Feb 22 '18 at 12:31
  • 2
    Thanks man. You saved my life!!! You deserve an Oscar!! I have been struggling to make it work for 5 days!!! – AkshayT Mar 23 '18 at 11:29
  • How to do vertical scroll instead Horizontal using this class ? – Mahesh Cheliya Jun 14 '18 at 07:04
  • 1
    @MaheshCheliya : Have a look at this answer. Maybe it is useful to you. https://stackoverflow.com/a/22797619/7874047 Note: All you need to change is, in that answer, they extend ViewPager. Instead of that, you need to extend CustomViewPager. – Ronak Thakkar Jun 14 '18 at 07:10
  • @RonakThakkar I need "one page will be loaded at once" thats why i am using this class, if i extend View Pager, it is always load minimum 2 page – Mahesh Cheliya Jun 14 '18 at 07:16
  • thats why i have written note in previous comment: Note: All you need to change is, in that answer, they extend ViewPager. Instead of that, you need to extend CustomViewPager. lets try once. – Ronak Thakkar Jun 14 '18 at 07:17
  • 2
    i tried to extends customeviewpager Instead ViewPager, but setPageTransformer method is not resolve in Customviewpager, so how to achieve vertical swipe with customeviewpager ? – Mahesh Cheliya Jun 15 '18 at 12:07
  • @MaheshCheliya : lets join this room : https://chat.stackoverflow.com/rooms/173214/room-for-ronak-mahesh-for-viewpager-issue – Ronak Thakkar Jun 15 '18 at 12:30
  • @RonakThakkar how to add this with TabLayout. Thanks for help in advance. – rupesh Oct 12 '18 at 19:02
  • @RonakThakkar I am using nested fragment. – rupesh Oct 12 '18 at 19:15
  • @RaRa how did you solve that problem? Can you please help me out with this? – rupesh Oct 12 '18 at 19:17
  • Actually, I haven't tried this with TabLayout yet. Once I do it for Tab I will edit the answer. – Ronak Thakkar Oct 16 '18 at 05:12
  • @rupesh check my answer, no need to add custom code, you can work with your existing code. – RaRa Oct 20 '18 at 07:48
  • 1
    Great solution, you save my day – Farmer Jan 04 '19 at 10:29
  • @RaRa t have you got any solution for this onetabLayout.setupWithViewPager(customeViewPager); here Tablayout not accepting the Custom class. – – rams Nov 15 '19 at 14:09
  • Hi @Prateek218 any solution for this one onetabLayout.setupWithViewPager(customeViewPager); here Tablayout not accepting the Custom class – rams Nov 15 '19 at 14:22
  • 1
    @RonakThakkar . Thanks a lot .It just saves my day.I was looking for making fragment dynamic for viewPager. But later i found that viewPager creates two instances or initiates two view at the starting time but i was bound to create just one.Your CustomViewPager class just did this amazingly. Now i can use a single fragment with different content in different page of viewPager. – Sakhawat Hossain Nov 19 '19 at 04:42
  • I got crash all the time `E/Parcel: Class not found when unmarshalling: android.support.v4.view.ViewPager$SavedState` does any one can help? – Bo Z Mar 06 '20 at 16:51
  • Does anyone know how to achieve the implementation of PageTransformer in this one? – Yekta Sarıoğlu Aug 30 '20 at 11:52
  • 1
    Great solution buddy, Worked for me. – Aman Gupta - ΔMΔN Nov 05 '20 at 09:35
  • Great solution for ViewPager with FragmentStatePagerAdapter – sharonooo Jan 11 '22 at 10:06
19

As far as I know, that is not possible when using the ViewPager. At least not, when you want your pages to be swipe-able.

The explaination therefore is very simple:

When you swipe between two pages, there is a Point when both pages need to be visible, since you cannot swipe between two things when one of those does not even exist at that point.

See this question for more: ViewPager.setOffscreenPageLimit(0) doesn't work as expected

CommonsWare provided a good explaination in the comments of his answer.

Community
  • 1
  • 1
Philipp Jahoda
  • 50,880
  • 24
  • 180
  • 187
  • 2
    What If I dont want the swipe feature of view pager ? . I am using NonSwipeableViewPager like this https://github.com/nuuneoi/TheCheeseLibrary/blob/master/src/main/java/com/inthecheesefactory/thecheeselibrary/view/NonSwipeableViewPager.java – John Aug 01 '16 at 08:23
  • 1
    Then don't use a viewpager ... Just use a fragment pager or a fragment state pager adapter with a tablayout – Kushan Feb 05 '17 at 10:11
13

but I need to load one page at a time because memory problems.

That presumes that you are getting OutOfMemoryErrors.

Am i going to have to use the old style tabhost etc?

Yes, or FragmentTabHost, or action bar tabs.

or is there a way/hack I can make my viewPager load one page at a time?

No, for the simple reason that ViewPager needs more than one page at a time for the sliding animation. You can see this by using a ViewPager and swiping.

Or, you can work on fixing your perceived memory problems. Assuming this app is the same one that you reported on earlier today, you are only using 7MB of heap space. That will only result in OutOfMemoryErrors if your remaining heap is highly fragmented. There are strategies for memory management (e.g., inBitmap on BitmapOptions for creating bitmaps from external sources) that help address such fragmentation concerns.

My Adapter extends BaseAdapter with the ViewHolder patern.

BaseAdapter is for use with AdapterView, not ViewPager.

Community
  • 1
  • 1
CommonsWare
  • 986,068
  • 189
  • 2,389
  • 2,491
6

I have an Answer for this. The above said method setUserVisibleHint() is deprecated and you can use setMaxLifecycle() method. For loading only the visible fragment you have to set the behaviour to BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT in the viewpager adapter. ie; in the Constructor. And for handling the fragment use onResume() method in the fragment.

In this way you can load only one fragment at a time in the viewpager.

public static class MyAdapter extends FragmentStatePagerAdapter {
public MyAdapter(FragmentManager fm) {
    super(fm, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT);
}

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

@Override
public Fragment getItem(int position) {
    return ArrayListFragment.newInstance(position);
}

}

In Kotlin:

class MyAdapter(fm: FragmentManager) : FragmentStatePagerAdapter(fm,BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT )

Also use with FragmentPagerAdapter (now deprecated) in same way

Rahul Mishra
  • 1,122
  • 1
  • 10
  • 25
rVarmag
  • 81
  • 1
  • 4
3

By using this method you can load one page at time in tab layout with view pager`

  @Override
    public void onResume() {
        super.onResume();
        if (getUserVisibleHint() && !isVisible) {
            Log.e("~~onResume: ", "::onLatestResume");
           //your code
        }
        isVisible = true;
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);

        if (isVisibleToUser && isVisible) {
            Handler handler = new Handler();
            handler.postDelayed(new Runnable() {
                @Override
                public void run() {
                   //your code
                }
            }, 500);

        }
    }

`

Zoe
  • 27,060
  • 21
  • 118
  • 148
Hardik Hirpara
  • 2,594
  • 20
  • 34
2

Override the setUserVisibleHint and add postDelayed like below in your every fragments.

override fun setUserVisibleHint(isVisibleToUser: Boolean) {
    if (isVisibleToUser)
        Handler().postDelayed({
            if (activity != null) {
                // Do you stuff here 
            }
        }, 200)
    super.setUserVisibleHint(isVisibleToUser)
}

I can manage by this way and its working fine now for me.

RaRa
  • 2,024
  • 1
  • 18
  • 29
1

First, copy in the SmartFragmentStatePagerAdapter.java which provides the intelligent caching of registered fragments within our ViewPager. It does so by overriding the instantiateItem() method and caching any created fragments internally. This solves the common problem of needing to access the current item within the ViewPager.

Now, we want to extend from SmartFragmentStatePagerAdapter copied above when declaring our adapter so we can take advantage of the better memory management of the state pager:

public abstract class SmartFragmentStatePagerAdapter extends FragmentStatePagerAdapter {
    // Sparse array to keep track of registered fragments in memory
    private SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

    public SmartFragmentStatePagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    // Register the fragment when the item is instantiated
    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    // Unregister when the item is inactive
    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    // Returns the fragment for the position (if instantiated)
    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}


// Extend from SmartFragmentStatePagerAdapter now instead for more dynamic ViewPager items
    public static class MyPagerAdapter extends SmartFragmentStatePagerAdapter {
    private static int NUM_ITEMS = 3;

        public MyPagerAdapter(FragmentManager fragmentManager) {
            super(fragmentManager);
        }

        // Returns total number of pages
        @Override
        public int getCount() {
            return NUM_ITEMS;
        }

        // Returns the fragment to display for that page
        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0: // Fragment # 0 - This will show FirstFragment
                return FirstFragment.newInstance(0, "Page # 1");
            case 1: // Fragment # 0 - This will show FirstFragment different title
                return FirstFragment.newInstance(1, "Page # 2");
            case 2: // Fragment # 1 - This will show SecondFragment
                return SecondFragment.newInstance(2, "Page # 3");
            default:
                return null;
            }
        }

        // Returns the page title for the top indicator
        @Override
        public CharSequence getPageTitle(int position) {
            return "Page " + position;
        }

    }
Zoe
  • 27,060
  • 21
  • 118
  • 148
0

You actually don't need a custom ViewPager.

I had the same issue and I did like this.

  • Keep the setOffscreenPageLimit() as 1.
  • Use fragment's onResume and onPause lifecycle methods.

Initialize and free-up memories on these lifecycle methods.

-2

I know this is an old post, but I stumbled upon this issue and found a good fix if your loading fragments. Simply, check if the user is seeing the fragment or not by overriding the setUserVisibleHint(). After that load the data.

    @Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        getData(1, getBaseUrl(), getLink());
    }
}
Ahmed Awad
  • 1,787
  • 3
  • 16
  • 22
  • 2
    A fragment can be visible but not initialized yet, this solution will cause issues. – Avamander Nov 25 '16 at 12:15
  • You can always add a boolean check and re-call it from the oncreate method. I've been using this solution for more than a month now, the app runs great. – Ahmed Awad Dec 04 '16 at 22:07