简体   繁体   English

Android ViewPager,屏幕旋转后标签不保持状态

[英]Android ViewPager with tab not maintaining state after screen rotation

I'm implementing a ViewPager with tabs similar to implementation provided by Google here . 我执行与标签类似,由谷歌提供的实施ViewPager 这里

My app has the following behaviour. 我的应用有以下行为。 My ViewPager has 3 "pages" (fragA, fragB, fragC) and I'm implementing 3 tabs on top of the ViewPager. 我的ViewPager有3个“页面”(fragA,fragB,fragC),我在ViewPager上面实现了3个标签。 The differece between google's implementation and mine is that the tabs aren't used to navigate between fragments. 谷歌的实现和我的实现之间的区别在于标签不用于在片段之间导航。 As an example, pressing one of my tabs, loads a different set of data on the visible fragment. 例如,按下我的一个选项卡,在可见片段上加载一组不同的数据。 I can have fragA with tab3 selected, fragB with tab2 selected and fragC with tab1 selected. 我可以选择tab3选择fragA,选择tab2选择fragB,选择tab1选择fragC。

The problem is when I rotate the screen. 问题是当我旋转屏幕时。 If I'm on FragmentA with the third tab selected, when I rotate the screen, I stay in the same fragment but now, the first tab is selected. 如果我在FragmentA上选择了第三个选项卡,当我旋转屏幕时,我会保持相同的片段,但现在,第一个选项卡被选中。 I want to maintain the same tab selected when rotating the screen. 我希望在旋转屏幕时保持选择相同的选项卡。

This is my class for managing the tabs: 这是我管理标签的类:

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());
    }
}

And this is the adapter that creates the fragments: 这是创建片段的适配器:

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;
    }
}

And finally, a Fragment (FragmentA, FragmentB and FragmentC) are almost identically: 最后,片段(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);
    }
}

The problem is that all my onCreateView are always called twice and the second time that they are called, the information is lost. 问题是我的所有onCreateView总是被调用两次,第二次调用它们时,信息就会丢失。 I've debugged and saw this behavior, 我已经调试并看到了这种行为,

When I rotate the screen, for example in FragmentA and with the third tab selected, this happens: 当我旋转屏幕时,例如在FragmentA中并选择第三个选项卡时,会发生以下情况:

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

In the 04 call (ManagerTabs -> onViewCreated), mTabHost.setCurrentTab(currentTab); 在04调用(ManagerTabs - > onViewCreated), mTabHost.setCurrentTab(currentTab); is called but inside onTabChanged , I get an java.lang.NullPointerException because all the fragments inside the adapter are all null. onTabChanged被调用,我得到一个java.lang.NullPointerException因为适配器内的所有片段都是null。

What can I do so that if I rotate the screen, I can maintain the current selected tab? 我该怎么办,如果我旋转屏幕,我可以保持当前选定的标签?

After screen rotation new instance of FragmentPagerAdapter is created. 屏幕旋转后,将创建FragmentPagerAdapter新实例。 But the ViewPager restores its state and state of all fragments it contains. ViewPager恢复其包含的所有片段的状态和状态。 ViewPager doesn't call adapters getView() method. ViewPager不调用适配器getView()方法。 Therefore Fragment[] currentFragment contains null values only. 因此, Fragment[] currentFragment仅包含null值。

You can get current fragment by another ways. 您可以通过其他方式获取当前片段。

If using FragmentPagerAdapter : 如果使用FragmentPagerAdapter

FragmentPagerAdapter sets tags on fragments it passes to FragmentManager . FragmentPagerAdapter在它传递给FragmentManager FragmentPagerAdapter设置标签。 This id depends on page index. 此id取决于页面索引。 How this id is generated you can see in source of the latest support lib . 如何生成此id,您可以在最新的支持库的源代码中看到。

To achieve this just add following method to your ManagerTabs fragment: 要实现此目的,只需在ManagerTabs片段中添加以下方法:

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

If using FragmentStatePagerAdapter : 如果使用FragmentStatePagerAdapter

It doesn't sets tags on fragments. 它不会在片段上设置标签。 With FragmentStatePagerAdapter it seems we can get by, using its instantiateItem(ViewGroup container, int position) call. 使用FragmentStatePagerAdapter ,我们可以使用其instantiateItem(ViewGroup container, int position)调用来获取。 It returns reference to fragment at position position . 它返回对位置position片段的引用。 If FragmentStatePagerAdapter already holds reference to fragment in question, instantiateItem just returns reference to that fragment, and doesn't call getItem() to instantiate it again. 如果FragmentStatePagerAdapter已经持有对相关片段的引用,则instantiateItem只返回对该片段的引用,并且不会调用getItem()再次实例化它。 In this case our method will be: 在这种情况下,我们的方法将是:

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

Finally: 最后:

Now use above method to get current fragment. 现在使用上面的方法来获取当前片段。 In your case: 在你的情况下:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM