I am using Android's support.v4
package to develop a ViewPager containing multiple Fragment
s.
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 Fragment
s 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);
I examined your code and I think the issue is that in
onPageScrolled
, you're callinggetItem
. 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 callgetItem
, 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 anArrayList
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.
Add the following when you initialize your
ViewPager
,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 theFragment
s even when they are not in focus.