I’m using a ViewPager that contains several ListViews, with code similar to that in the answer for Infinite ViewPager. The idea is to have something like the day view for the Google Calendar app (whose source seems to be unavailable; only the default calendar app’s is but it uses a ViewSwitcher) – I want to make it seem like the user can swipe infinitely left and right, but there are actually only 3 items in the ViewPager, and when the user hits page 0 or 2, we set 1 as the current page and update accordingly.
Now, this all works. However, strangely, when the phone is rotated and the activity is rebuilt (I’m avoiding using configChanges for now), the pages in the app are instantiated again, but out of order. Instead of 0->1->2, the order is 1->0->2, and this screws up the order of the pages in the app.
My Fragment, in onActivityCreated():
mPagerAdapter = new ContinuousPagerAdapter(R.layout.my_listview, this);
// set the adapter
mViewPager = (ViewPager) getView().findViewById(R.id.agendaViewPager);
mViewPager.setOffscreenPageLimit(3);
mViewPager.setOnPageChangeListener(this);
mViewPager.setAdapter(mPagerAdapter);
mViewPager.setSaveEnabled(false);
// ...
mViewPager.setCurrentItem(1, false);
loadData();
The Pager Adapter:
import android.content.Context;
import android.os.Parcelable;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
public class ContinuousPagerAdapter extends PagerAdapter {
OnPageInstantiatedListener pListener;
ViewPager container;
int childLayoutResId;
@SuppressWarnings("unused")
private ContinuousPagerAdapter() {
}
/**
* @param childLayoutResId Layout resource ID of the children to be inflated
*/
public ContinuousPagerAdapter(int childLayoutResId, OnPageInstantiatedListener pListener) {
this.childLayoutResId = childLayoutResId;
this.pListener = pListener;
}
@Override
public int getCount() {
return 3;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
@Override
public Object instantiateItem(View container, int position) {
this.container = (ViewPager) container;
// inflate a new child view
LayoutInflater li = (LayoutInflater) container.getContext().getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
View childView = li.inflate(childLayoutResId, null, false);
// add it to the view pager and return
int count = this.container.getChildCount();
int actualPos = count > position ? position : count;
this.container.addView(childView, actualPos);
pListener.onPageInstantiated(actualPos); // sometimes use 0 instead of actualPos, with different but still inconsistent results
return childView;
}
@Override
public void destroyItem(View container, int position, Object object) {
((ViewPager) container).removeViewAt(position);
}
public static interface OnPageInstantiatedListener {
public void onPageInstantiated(int position);
}
/**
* Needed to ensure all the items are instantiated
*/
@Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
@Override
public Parcelable saveState() {
return null;
}
@Override
public void restoreState(Parcelable state, ClassLoader loader) {
}
@Override
public void finishUpdate(View container) {
}
}
I don’t understand why the pages are instantiated in the 1->0->2 order after rotation. I’m not saving state either. Any insights into this would be helpful.
After meticulously debugging the app and looking through the
ViewPagersource, I found the problem.The first time the app starts and
mViewPager.setAdapter(mPagerAdapter)is called, the pages are instantly initiated and the app works as it should. However, when the phone is rotated, callingsetAdapter()postpones instantiating the pages becausegetWindowToken()returns null as the window is not ready yet. Instantiating is delayed until even afteronResume()is called in some loop.Calling
setCurrentItem(1, false)makes the first page the primary page, and as a result it is instantiated before the other pages, resulting in the at-first strange 1->0->2 instantiation.The solution? Use a
Handlerto run thesetCurrentItem()and load data after the other pages have been instantiated:Though I’d normally want to avoid using
Handlers, it seems this is the only option I’ve found thus far, because the pages themselves are added in a looper.EDIT: Even the above had some issues. I ended up calling
mViewPager.setCurrentItem(1, false)only after all pages have been instantiated (inonPageInstantiated()).