【问题标题】:Android ViewPager with tab not maintaining state after screen rotation屏幕旋转后带有选项卡的 Android ViewPager 不保持状态
【发布时间】:2014-12-23 11:37:41
【问题描述】:

我正在实现一个带有标签的 ViewPager,类似于 Google here 提供的实现。

我的应用具有以下行为。我的 ViewPager 有 3 个“页面”(fragA、fragB、fragC),我在 ViewPager 上实现了 3 个选项卡。 google 的实现和我的不同之处在于标签不用于在片段之间导航。例如,按下我的一个选项卡,在可见片段上加载一组不同的数据。我可以选择 tab3 的 fragA,选择 tab2 的 fragB 和选择 tab1 的 fragC。

问题是当我旋转屏幕时。如果我在FragmentA 上选择了第三个选项卡,当我旋转屏幕时,我会停留在同一个片段中,但现在,第一个选项卡被选中。我想在旋转屏幕时保持选择的相同选项卡。

这是我管理标签的课程:

public class ManagerTabs extends Fragment {

    private TabHost.TabContentFactory mFactory = new TabHost.TabContentFactory() {

        @Override
        public View createTabContent(String tag) {
            View v = new View(getActivity());
            v.setMinimumWidth(0);
            v.setMinimumHeight(0);
            return v;
        }
    };

    public static ManagerTabs newManager() {
        return new ManagerTabs();
    }


    @Override
    public View onCreateView(LayoutInflater inflater,  ViewGroup container,
     Bundle savedInstanceState) {
        view = inflater.inflate(R.layout.datatabs, container, false);
        createTabs();
        createPager();
        return view;
    }

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

        if (savedInstanceState != null) {
            currentPage = savedInstanceState.getInt("currentPage", currentPage);
            currentTab = savedInstanceState.getInt("currentTab", currentTab);
            mTabHost.setCurrentTab(currentTab);
        }
        mPager.setCurrentItem(currentPage);
    }

    private void createPager() {
        mPager = (ViewPager) view.findViewById(R.id.datapager);
        pagerAdapter = new PagerAdapter(getChildFragmentManager());
        mPager.setAdapter(pagerAdapter);

        IconPageIndicator indicator = (IconPageIndicator) rootView.findViewById(R.id.indicator);
        indicator.setViewPager(pager);
        indicator.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
            @Override
            public void onPageSelected(int position) {
                currentTab = mTabHost.getCurrentTab();

                currentPage = mPager.getCurrentItem();

                switch (currentPage) {
                case 0:
                    ((FragmentA) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab);
                    break;
                case 1:
                    ((FragmentB) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab);
                    break;
                case 2:
                    ((FragmentC) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab);
                    break;
                }
            }
        });
    }

    private void createTabs() {

        mTabHost = (TabHost) view.findViewById(android.R.id.tabhost);
        mTabHost.setup();

        mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.day))setIndicator(getString(R.string.day)).setContent(mFactory));
        mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.month)).setIndicator(getString(R.string.month)).setContent(mFactory));
        mTabHost.addTab(mTabHost.newTabSpec(getString(R.string.year)).setIndicator(getString(R.string.year)).setContent(mFactory));

        mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
            public void onTabChanged(String tag) {
                currentTab = mTabHost.getCurrentTab();
                currentPage = mPager.getCurrentItem();
                switch (currentPage) {
                case 0:
                    ((FragmentA) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab);
                    break;
                case 1:
                    ((FragmentB) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab);
                    break;
                case 2:
                    ((FragmentC) pagerAdapter.getCurrentFragment(currentPage)).getData(currentTab);
                    break;
                }
            }
        });
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("currentPage", mPager.getCurrentItem());
        outState.putInt("currentTab", mTabHost.getCurrentTab());
    }
}

这是创建片段的适配器:

public class PagerAdapter extends FragmentPagerAdapter implements IconPagerAdapter{

    private Fragment[] currentFragment = new Fragment[3];

    public PagerAdapter(FragmentManager fm) {
        super(fm);
    }

    @Override
    public Fragment getItem(int index) {
        Fragment fragment;
        switch (index) {
        case 0:
            fragment = FragmentA.newInstance();
            break;
        case 1:
            fragment = FragmentB.newInstance();
            break;
        case 2:
            fragment = FragmentC.newInstance();
            break;
        default:
            fragment = null;
            break;
        }

        currentFragment[index] = fragment;

        return fragment;
    }

    @Override
    public int getIconResId(int index) {

        switch (index) {
        case 0:
            return R.drawable.fraga;
        case 1:
            return R.drawable.fragb;
        case 2:
            return R.drawable.fragc;
        default:
            return -1;
        }
    }

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

    public Fragment getCurrentFragment(int currentFrag) {
        return currentFragment[currentFrag];
    }

    public Fragment[] getCurrentFragment() {
        return currentFragment;
    }
}

最后,一个 Fragment(FragmentA、FragmentB 和 FragmentC)几乎完全相同:

public class FragmentA extends Fragment {

    public static FragmentA newInstance() {
        return new FragmentA();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.datafragment, container, false);

        mTabHost = (TabHost) ((View) container.getParent().getParent()).findViewById(android.R.id.tabhost);

        if (savedInstanceState != null) {
            currentTab = savedInstanceState.getInt("currentTab");
        }
        populateLayout(currentTab);

        return rootView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("currentTab", currentTab);
    }
}

问题是我所有的onCreateView 总是被调用两次,而第二次调用它们时,信息就会丢失。我已经调试并看到了这种行为,

当我旋转屏幕时,例如在 FragmentA 中并选择第三个选项卡时,会发生这种情况:

01  ManagerTabs -> onSaveInstanceState
02  FragmentA -> onSaveInstanceState
03  ManagerTabs -> onCreateView
04  ManagerTabs -> onViewCreated

在 04 调用(ManagerTabs -> onViewCreated)中,mTabHost.setCurrentTab(currentTab); 被调用,但在 onTabChanged 内部,我得到一个 java.lang.NullPointerException,因为适配器内的所有片段都是空的。

如果我旋转屏幕,我该怎么做才能保持当前选定的选项卡?

【问题讨论】:

  • 在活动中覆盖onSaveInstanceState并从活动中设置数据
  • 你好。运气不好
  • 你是否发布了与Fragment.onDestroyView()中的Activity相关的所有内容?
  • 另外,你可能想看看这个:stackoverflow.com/questions/11160412/…
  • 你好。谢谢。至少如果我知道,我不会那样做。我使用的代码都在问题中。我试过 setretaininstance 但没有成功

标签: android android-fragments tabs android-viewpager


【解决方案1】:

屏幕旋转后,FragmentPagerAdapter 的新实例被创建。但是ViewPager 恢复了它的状态和它包含的所有片段的状态。 ViewPager 不调用适配器 getView() 方法。因此Fragment[] currentFragment 仅包含null 值。

您可以通过其他方式获取当前片段。

如果使用FragmentPagerAdapter

FragmentPagerAdapter 在它传递给FragmentManager 的片段上设置标签。此 id 取决于页面索引。这个 id 是如何生成的,你可以在 source of the latest support lib 看到。

要实现这一点,只需将以下方法添加到您的 ManagerTabs 片段:

public Fragment getCurrentPagerFragment(int position) {
    String fragmentTag = "android:switcher:" + mPager.getId() + ":" + position;
    return getChildFragmentManager().findFragmentByTag(fragmentTag);
}

如果使用FragmentStatePagerAdapter

它不会在片段上设置标签。使用FragmentStatePagerAdapter 似乎我们可以通过instantiateItem(ViewGroup container, int position) 调用。它返回对位置position 的片段的引用。如果FragmentStatePagerAdapter 已经拥有对相关片段的引用,instantiateItem 只会返回对该片段的引用,并且不会调用getItem() 来再次实例化它。在这种情况下,我们的方法是:

public Fragment getCurrentPagerFragment(int position) {
    FragmentStatePagerAdapter a = (FragmentStatePagerAdapter) mPager.getAdapter();
    return (Fragment) a.instantiateItem(pager, position);
}

最后:

现在使用上述方法获取当前片段。在你的情况下:

mTabHost.setOnTabChangedListener(new TabHost.OnTabChangeListener() {
    public void onTabChanged(String tag) {
        currentTab = mTabHost.getCurrentTab();
        currentPage = mPager.getCurrentItem();
        Fragment currentFragment = getCurrentPagerFragment(currentPage);
        switch (currentPage) {
            case 0:
                ((FragmentA) currentFragment).getData(currentTab);
                break;
            case 1:
                ((FragmentB) currentFragment).getData(currentTab);
                break;
            case 2:
                ((FragmentC) currentFragment).getData(currentTab);
                break;
        }
    }
});

【讨论】:

  • 谢谢。我有java.lang.NullPointerException 因为getCurrentPagerFragment 返回NULL。如果我调试我可以看到 getFragmentManager() 字段 mActive -> [0] -> mChildFragmentManager -> mActive 作为我需要的片段,但我不知道如何获得它们。您能否提供有关字符串 fragmentTag 的更多详细信息?
  • 哦,对不起。我的代码中有一个小错误。我们应该使用getChildFragmentManager() 而不是getFragmentManager()。我添加了一些解释并更新了我的答案。
  • 抱歉反馈迟了。非常感谢。我有一个类似的问题,但有不同的实现here。如果你可以看看
猜你喜欢
  • 1970-01-01
  • 2016-01-04
  • 2011-08-06
  • 2011-01-31
  • 2017-01-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多