Reputation: 1996
So it appears that when using a ViewPager, the onPageSelected listener does not get called for the first page same issue as this.
I have some logic that populates some more expensive UI elements for the currently selected page and this works when page is changed, but it doesn't work for the first page.
If I set the current item after the listener, the callback gets fired for the first page, but the view has not been initialized yet, so I can't manipulate it:
// Inside PagerAdapter.instantiateItem
ViewHolder vh = new ViewHolder();
cursor.moveToPosition(position);
vh.view = adapter.newView(context, cursor, null);
// Set position as tag so we can retrieve it with findViewByTag
vh.view.setTag(position);
((ViewPager) collection).addView(vh.view,0);
return vh;
// Inside MyActivity.onCreate
pagerAdapter = new SingleMessagePagerAdapter(this, cursor);
pager = (ViewPager)findViewById(R.id.message_pager);
pager.setAdapter(pagerAdapter);
pager.setOnPageSelectedListener(this);
pager.setCurrentItem(selectedItem);
// Inside MyActivity.onPageSelected
// Retrieve tagged view
View view = pager.findViewWithTag(position);
Here view
ends up being null because PagerAdapter.instantiateItem
has not yet been run. So I guess my question is, at which point in the activity lifecycle can I be certain that the ViewPager has initialized the view? I tried doing this inside Activity.onAttachedToWindow
and Activity.onResume
but it appears both of these get fired before PagerAdapter.instantiateItem
.
Upvotes: 23
Views: 14306
Reputation: 718
My solution was to extend pager adapter and create an interface inside it. Then make the adapter call the interface only once after creating the adapter. Inside interface callback you can call onPageChanged method without having nullpointerexception. Add this class to your project and extend your adapter from it. Dont forget to set listener to adapter before setting adapter to viewpager. Adapter class below:
public abstract class ExtendedPagerAdapter extends FragmentPagerAdapter {
private boolean instantiated;
private AdapterListener adapterListener;
public interface AdapterListener {
void onAdapterInstantiated();
}
public ExtendedPagerAdapter(FragmentManager fragmentManager) {
this(fragmentManager, null);
}
public ExtendedPagerAdapter(FragmentManager fragmentManager, AdapterListener adapterListener) {
super(fragmentManager);
this.adapterListener = adapterListener;
instantiated = false;
}
@Override
public void finishUpdate(ViewGroup container) {
super.finishUpdate(container);
if (!instantiated) {
instantiated = true;
if (adapterListener != null) {
adapterListener.onAdapterInstantiated();
}
}
}
public void setAdapterInstantiatedListener(AdapterListener adapterListener) {
this.adapterListener = adapterListener;
}
}
Activity code:
adapter = new ViewPagerAdapter(getChildFragmentManager());
adapter.setAdapterInstantiatedListener(new ExtendedPagerAdapter.AdapterListener() {
@Override
public void onAdapterInstantiated() {
onPageSelected(viewPager.getCurrentItem());
}
});
viewPager.addOnPageChangeListener(this);
viewPager.setAdapter(adapter);
Upvotes: 1
Reputation: 607
try to use fragments!
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter() {
super(getSupportFragmentManager());
}
@Override
public Fragment getItem(int i) {
Fragment fr = null;
if (i==0)
fr = new sec0frag();
else if (i==1)
fr = new sec1frag();
else if (i==2)
fr = new sec2frag();
return fr;
}
@Override
public int getCount() {
return 3;
}
}
and create 3 fragments classes
e.g. :
public static class sec0frag extends Fragment {
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// here is initialize for first time to view pages.!
}
}
Upvotes: 2
Reputation: 1554
I'm wondering why you don't just have the logic you mention in the Fragment itself rather than the hosting Activity. ViewPager buffers a couple of fragments either side of the current one so they're set up in the background and ready to go when the user swipes to them. Putting the logic in onPageSelected
would mean bypassing this functionality and not doing the heavy lifting until the user swipes to the page.
Assuming for some reason you can't do the above, why not use an Interface with a callback function. Trigger the callback in the fragment's onCreateView
function to let the Activity know it's fully instantiated.
Upvotes: 5