问题
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.
回答1:
After meticulously debugging the app and looking through the ViewPager
source, 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, calling setAdapter()
postpones instantiating the pages because getWindowToken()
returns null as the window is not ready yet. Instantiating is delayed until even after onResume()
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 Handler
to run the setCurrentItem()
and load data after the other pages have been instantiated:
new Handler().post(new Runnable() {
@Override
public void run() {
mViewPager.setCurrentItem(1, false);
cleanupAndShowData();
}
});
Though I'd normally want to avoid using Handler
s, 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 (in onPageInstantiated()
).
回答2:
actualPos= position%getCount();
If this doesn't help, please give some additional code.
I would also suggest a little modification
mViewPager.setOffscreenPageLimit(2); //use 2 instead of 3 unless neccessary
来源:https://stackoverflow.com/questions/13585396/viewpager-re-instantiates-items-out-of-order-after-screen-rotation