【问题标题】:ViewPager inside fragment, how to retain state?ViewPager里面的片段,如何保留状态?
【发布时间】:2015-01-15 14:44:18
【问题描述】:

在我的应用程序中,片段活动包含两个片段,片段 A 和片段 B。片段 B 是一个包含 3 个片段的视图分页器。

在我的活动中,为了防止在配置更改时重新创建片段:

if(getSupportFragmentManager().findFragmentByTag(MAIN_TAB_FRAGMENT) == null) {
    getSupportFragmentManager().beginTransaction().replace(R.id.container, new MainTabFragment(), MAIN_TAB_FRAGMENT).commit();
}

片段 B 的代码:

public class MainTabFragment extends Fragment {

    private PagerSlidingTabStrip mSlidingTabLayout;
    private LfPagerAdapter adapter;
    private ViewPager mViewPager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_tab, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {

        this.adapter = new LfPagerAdapter(getChildFragmentManager());

        this.mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
        this.mViewPager.setAdapter(adapter);

        this.mSlidingTabLayout = (PagerSlidingTabStrip) view.findViewById(R.id.sliding_tabs);
        this.mSlidingTabLayout.setViewPager(this.mViewPager);
    }
}

适配器代码:

public class LfPagerAdapter extends FragmentPagerAdapter {

    private static final int NUM_ITEMS = 3;

    private FragmentManager fragmentManager;

    public LfPagerAdapter(FragmentManager fm) {
        super(fm);
        this.fragmentManager = fm;
    }

    @Override
    public int getCount() {
        return NUM_ITEMS;
    }

    @Override
    public Fragment getItem(int position) {
        Log.d("TEST","TEST");
        switch (position) {
            case 1:
                return FragmentC.newInstance();
            case 2:
                return FragmentD.newInstance();
            default:
                return FragmentE.newInstance();
        }
    }
}

我的问题是我无法在方向更改时保留视图寻呼机及其子片段的状态。

显然,这在每次旋转时都会调用:

this.adapter = new LfPagerAdapter(getChildFragmentManager());

这将导致重新创建整个寻呼机,对吗?结果

getItem(int position)

将在每次旋转时调用,片段将从头开始创建并丢失其状态:

return FragmentC.newInstance();

我尝试用以下方法解决这个问题:

if(this.adapter == null)
    this.adapter = new LfPagerAdapter(getChildFragmentManager());

在onViewCreated 中,但结果是在旋转时,寻呼机内的片段被移除。

任何想法如何正确保留寻呼机内的状态?

【问题讨论】:

  • 我通过在 instanceState 中保存片段状态(和选定的寻呼机位置)来做到这一点(不知道确切的覆盖,因为我现在无法访问 Eclipse)。然后在 OnCreate(如果在活动中)或 OnCreateView()(如果在片段中),我检查它们是否存在,然后重新创建它们。
  • Fragments 由FragmentManager 自动管理和恢复,PagerAdapter 实现旨在保持对它们的持久引用。 getItem() 只会被调用一次来初始化每个页面。
  • @artworkad:Fragments 好像已经被保留了。你到底想做什么?你能再描述一下吗?

标签: android android-fragments fragmentpageradapter


【解决方案1】:

你需要做两件事来解决这个问题:

1) 您应该使用onCreate 方法而不是onViewCreated 来实例化LfPagerAdapter;

即:

    public class MainTabFragment extends Fragment {

    private PagerSlidingTabStrip mSlidingTabLayout;
    private LfPagerAdapter adapter;
    private ViewPager mViewPager;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setRetainInstance(true);
        this.adapter = new LfPagerAdapter(getChildFragmentManager());
    }

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_tab, container, false);
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {


        this.mViewPager = (ViewPager) view.findViewById(R.id.viewpager);
        this.mViewPager.setAdapter(adapter);

        this.mSlidingTabLayout = (PagerSlidingTabStrip) view.findViewById(R.id.sliding_tabs);
        this.mSlidingTabLayout.setViewPager(this.mViewPager);
    }
}

2) 您需要扩展 FragmentStatePagerAdapter 而不是 FragmentPagerAdapter

【讨论】:

  • FragmentPagerAdapter 从不分离Fragments,因此它们像往常一样由FragmentManager 自动管理和保留,并且两个适配器都保持对它们的持久引用。保留Fragment 实例不是必需的,实际上甚至可能在某些方面干扰保留Views 和/或子Fragments 状态的语义。
  • @corsair992:那么需要做什么来解决这个问题?
  • 没什么——一开始就没有问题。如果Fragment 实例未被保留,那么ViewPager 和子Fragments 的状态应该已经被管理(尽管您可能需要正确设置它以保留当前页面)。
  • @corsair992:不保留还是保留?我没明白你的意思。
  • 正如我在第一条评论中所说:“保留Fragment 实例不是必需的,实际上甚至可能在某些方面干扰保留Views 和/或孩子Fragments。”。我说的是Fragment初始化代码中的setRetainInstance(true);调用。
【解决方案2】:

Android 将自动在配置中重新创建您的活动,而您的清单中没有此行 android:configChanges="keyboardHidden|orientation|screenSize",以便您可以自己处理 onConfiguration 更改。

唯一的方法是在你的activityFragement中使用onSaveInstanceState()来保存viewPager状态(例如当前位置)和你需要保存东西的片段

如何保存viewpager的当前位置并恢复它onConfiguration更改的示例

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
            int position = mViewPager.getCurrentItem();
            outState.Int("Key", position );
    }

    @Override //then restore in on onCreate();
    public void onCreated(Bundle savedInstanceState) {
        super.onCreated(savedInstanceState);
        // do stuff
        if (savedInstanceState != null) {
              int  position= savedInstanceState.getInt("Key");
            mViewPager.setCurrentItem(position)
        }
    }

当然,这是一个非常基本的例子。

Ps:在片段中恢复使用 onActivityCreated() 而不是 onCreate() 方法。

这是另一个关于如何保留状态的示例:Click me!

【讨论】:

  • 实际上,ViewPager 自己管理当前位置的保存和恢复,如果适配器在View 状态恢复后的适当时间设置。
  • 我知道这是不久前的事了,但是@corsair992 你能扩展一下吗?这听起来正是我所需要的。
  • @D_Steve595:查看最新版本支持库中ViewPager 的源代码,它现在似乎应该在所有情况下正确处理持久化位置和适配器状态。您对此有任何疑问吗?
  • 感谢您的回复!我最终提出了一个问题,并在这里得到了回答:stackoverflow.com/questions/29464522/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多