ViewPager re-instantiates items out of order after screen rotation

匿名 (未验证) 提交于 2019-12-03 09:06:55

问题:

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 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 (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 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!