【问题标题】:ViewPager re-instantiates items out of order after screen rotationViewPager 在屏幕旋转后重新实例化项目
【发布时间】:2013-01-04 15:58:23
【问题描述】:

我正在使用包含多个 ListViews 的 ViewPager,其代码类似于 Infinite ViewPager 的答案中的代码。这个想法是为谷歌日历应用程序提供类似日视图的东西(其来源似乎不可用;只有默认日历应用程序是,但它使用ViewSwitcher) - 我想让它看起来像用户可以无限滑动左右,但ViewPager中实际上只有3个项目,当用户点击页面0或2时,我们将1设置为当前页面并相应更新。

现在,这一切都奏效了。然而,奇怪的是,当手机旋转并重建活动时(我暂时避免使用configChanges),应用程序中的页面会再次实例化,但无序。顺序不是 0->1->2,而是 1->0->2,这会打乱应用中页面的顺序。

我的片段,在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();

寻呼机适配器:

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) {
    }
}

我不明白为什么页面在旋转后以 1->0->2 的顺序实例化。我也没有保存状态。对此的任何见解都会有所帮助。

【问题讨论】:

  • 您可以发布其余的片段代码吗?
  • 其他代码与此问题无关;但是我已经想通了。

标签: android user-interface android-fragments android-viewpager android-pageradapter


【解决方案1】:

在仔细调试应用程序并查看ViewPager源后,我发现了问题。

应用程序第一次启动并调用mViewPager.setAdapter(mPagerAdapter) 时,页面会立即启动并且应用程序可以正常运行。但是,当手机旋转时,调用setAdapter() 会推迟实例化页面,因为getWindowToken() 返回null,因为窗口还没有准备好。实例化被延迟,直到在某个循环中调用onResume() 之后

调用setCurrentItem(1, false) 使第一页成为主页面,因此它在其他页面之前被实例化,导致一开始奇怪的 1->0->2 实例化。

解决方案?使用Handler 运行setCurrentItem() 并在其他页面实例化后加载数据:

new Handler().post(new Runnable() {
    @Override
    public void run() {
        mViewPager.setCurrentItem(1, false);
        cleanupAndShowData();
    }
});

虽然我通常希望避免使用Handlers,但似乎这是迄今为止我找到的唯一选项,因为页面本身是添加到一个活套中的。

编辑:即使是上面的也有一些问题。只有在所有页面都被实例化后(在 onPageInstantiated() 中),我才最终调用 mViewPager.setCurrentItem(1, false)

【讨论】:

    【解决方案2】:
    actualPos= position%getCount();
    

    如果这没有帮助,请提供一些额外的代码。

    我也建议稍微修改一下

    mViewPager.setOffscreenPageLimit(2);   //use 2 instead of 3 unless neccessary
    

    【讨论】:

    • 感谢您的回答。但是,就放置而言,第一段代码并没有太大意义。我只是用“0”代替。此外,出于某种原因,屏幕外页面限制为 3 - 我正好需要显示 3 个页面。
    • 页数限制是屏幕外,所以1可见+2屏幕外是应该做的
    • 也许你是对的,但我看到的例子使用 3(即stackoverflow.com/questions/12538861/…)。我会测试一下并告诉你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多