17

I am using Android's support.v4 package to develop a ViewPager containing multiple Fragments.

I am trying to hide the Views when the user changes page. So, I implemented an OnPageChangeListener to listen to my adapter, and call an update() method in my Fragments when when a page change occurs. But I'm getting a NullPointerException, and I cannot figure out why.

Here is the code:

public class MyActivity extends FragmentActivity implements
        OnPageChangeListener {

    private static final int NUM_ITEMS = 2;
    private ViewPager mPager;
    private static SpaceAdapter mAdapter;

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.fragment_pager);

        mAdapter = new SpaceAdapter(getSupportFragmentManager());
        mPager = (ViewPager) findViewById(R.id.pager);
        mPager.setAdapter(mAdapter);

        mPager.setOnPageChangeListener(this);
}

    public static class SpaceAdapter extends FragmentPagerAdapter {

        public SpaceAdapter(FragmentManager fm) {
            super(fm);
        }

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

        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0:
            return new MyFragment();
            case 1:
            return new MyFragment();
            default:
            return new MyFragment();
            }
        }
    }

    @Override
    public void onPageScrollStateChanged(int arg0) {
        Log.i("pagination", "scroll state changed: " + arg0);
}

    @Override
    public void onPageScrolled(int arg0, float arg1, int arg2) {
        // Log.i("pagination", "page scroll: " + arg0 + " with: " + arg1 + ", " + arg2);
        if (mAdapter.getItem(arg0) instanceof IUpdateOnPage) {
            ((IUpdateOnPage) mAdapter.getItem(arg0)).updateOnPage();
        }
    }

    @Override
    public void onPageSelected(int arg0) {
        Log.i("pagination", "page selected: " + arg0);
    }
}

and

public class MyFragment extends Fragment implements
        SurfaceHolder.Callback, IUpdateOnPage {

    private SurfaceView charts;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View v = inflater.inflate(R.layout.crew, null);
        bindViews(v);
        return v;
    }

    private void bindViews(View v) {
        charts = (SurfaceView) v.findViewById(R.id.civ_charts);

        charts.getHolder().addCallback(this);
        Log.i("wtf", "huh " + String.valueOf(charts == null)); // logs false
    }

    @Override
    public void updateOnPage() {
        if (charts != null) {
            Log.i("crew surface", "hidden");
            charts.setVisibility(View.GONE);
        } else {
            Log.i("crew surface", "charts is null");  //THIS HERE gets logged
        }
    }
}

wWhen I copy/pasted I removed the SurfaceHolder.Callback methods, but they are there. The bindViews(v) method logs if charts == null, and that always returns false. When I page, updateOnPage gets called, and charts == null returns true. Why does this happen and how do I get around it?

and this is line 108:

charts.setVisibility(View.GONE);
Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
edthethird
  • 6,263
  • 2
  • 24
  • 34
  • ok, after further investigation, I am assuming that the fragment gets destroyed when pagination begins (even though the view remains?). when swiping I can still see "half" the fragment until the new fragment snaps into full-view. So, if all the references are getting collected and all that's left is the view, how can I do this? – edthethird Mar 25 '12 at 03:35
  • if you are getting a `NullPointerException`, can you post the logcat output? – Alex Lockwood Mar 25 '12 at 04:14
  • well I'm actually catching it so an exception isn't thrown. The problem is `charts.setVisibility(View.GONE);`. charts is null. It's at the very end of the second sample of code, right above the `else`. Added stack trace. – edthethird Mar 25 '12 at 04:18

2 Answers2

37

Add the following when you initialize your ViewPager,

mPager.setOffscreenPageLimit(NUM_ITEMS-1);

In your case, this will set the number of off-screen pages to 1 (i.e. it will keep the additional off-screen page in memory when the other page is currently in focus). This will force your ViewPager to keep all of the Fragments even when they are not in focus.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
  • that fixed another minor performance issue I was having so thank you! But it still crashes when this fragment is shown with the same null pointer.. – edthethird Mar 25 '12 at 04:42
  • actually, that fixes the core issue. I don't need to call setVisibilty(View.gone) anymore. Strange, but thanks! Just to clarify-- the SurfaceView wasn't paging properly, it remained on all pages even when I swiped. – edthethird Mar 25 '12 at 04:43
  • That's strange because `setOffscreenPageLimit` defaults to 1 (and NUM_ITEMS - 1 would equal 1) in v4, but I'm glad your problem has been solved. – louielouie Mar 25 '12 at 04:51
  • actually I "dumbed" down my code a bit to paste it here. I have 7 fragments, but this is the only one with a surfaceview, and the only one where the issue is... Thanks though! – edthethird Mar 25 '12 at 04:53
  • glad to hear you got it working. the `ViewPager` life-cycle isn't very well documented in my opinion... it can be kind of tricky. – Alex Lockwood Mar 25 '12 at 05:26
  • Hey Alex, I'm not sure why you keep deleting text from this post, but please stop. This website is meant to be a resource, and there is no need to censor it. – edthethird May 05 '12 at 22:18
  • Sorry if you felt like I was censoring your post. I edited your post because a lot of the information you provided ended up being irrelevant to the problem you were having. My hopes were that people who have similar problems don't have to sift through a bunch of unnecessary code. Feel free to keep it as is if you feel posting the entire second half of your post is necessary for those who also have this issue and are wondering how to fix it. – Alex Lockwood May 05 '12 at 22:31
  • Thanks a lot @AlexLockwood ! your solution helped me solve this problem I had been looking into for more than an hour :) – Muaz Othman Dec 06 '12 at 17:43
  • Wow!! This is just what I'm looking for. I was looking for rising that number and this just makes the trick. My life is a lot of easier right now!! – Sotti Jun 19 '14 at 10:54
4

I examined your code and I think the issue is that in onPageScrolled, you're calling getItem. This implementation (which is actually like most implementations of it on web) returns a new instance each time. However, most examples I've seen don't call getItem, so this is usually alright.

In your case, I would hold lazily create the Fragment in getItem: if it doesn't exist for that position, then create it, and then hang onto it in an ArrayList or some other collection, but if it already exists, just return it from that collection.

Otherwise, you create a fragment instance A, then scroll to another fragment instance B. You try to tell fragment instance A to clean up some views, but you wind up telling a new fragment instance C to do so. It hasn't had a chance to set up its own views yet, so you're getting the null pointer to charts.

Alex Lockwood
  • 83,063
  • 39
  • 206
  • 250
louielouie
  • 14,881
  • 3
  • 26
  • 31
  • oh that's a good point. I got this implementation from a google sample somewhere, but I will investigate implementing an array or something to maintain the fragments. Thanks! – edthethird Mar 25 '12 at 04:55