屏幕旋转时,ViewPager片段消失

31

我正在开发一个包含ViewPagerFragment的Android应用程序,结构如下:

MainActivity(MainActivityFragment(screenSlideViewPager(Fragments))),这意味着:

Activity 包含 Fragment 包含 ViewPager 包含 Fragments

现在我的问题是,当旋转设备或更改屏幕方向时,ViewPager中的所有Fragment都会消失。

有什么解决方法吗?

编辑1

MainActivity.java:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    if (getSupportFragmentManager().findFragmentByTag(TAG) == null) {
        final FragmentTransaction ft = getSupportFragmentManager()
                .beginTransaction();
        ft.add(android.R.id.content, new MainActivityFragment(), TAG);
        ft.commit();
    }
}

MainActivityFragment.java:

public class MainActivityFragment extends Fragment {
    private ViewPager mPager = null;
    private PagerAdapter mPageAdapter = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View reVal = inflater.inflate(R.layout.activity_main, container, false);
        mPager = (ViewPager) reVal.findViewById(R.id.pagerMainContent);
        mPageAdapter = new ScreenSlidePagerAdapter(getActivity().getFragmentManager());
        mPager.setAdapter(mPageAdapter);
        return reVal;
    }

    private MainGridViewFragment mainGridFragment;
    private AlphabetGridViewFragment alphabetGridFragment;

    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public android.app.Fragment getItem(int position) {
            switch (position) {
                case 1:
                    mainGridFragment = new MainGridViewFragment();
                    return mainGridFragment;
                case 0:
                    alphabetGridFragment = new AlphabetGridViewFragment();
                    return alphabetGridFragment;
                default:
                    return null;
            }
        }

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

EDIT 2 屏幕旋转后的活动

显然,MainActivityMainActivityFragment已经加载,证明是由于出现了ActionBar。请注意,ViewPager也已经加载,因为您仍然可以在页面之间进行导航(蓝色指示灯表示分页器已到达最后一页),但是您无法看到其内容。


当安卓屏幕旋转时,你的活动会被销毁和重新创建。因此,你需要保存变量等状态,以便可以“从你离开的地方”继续进行。这里有一个参考链接:http://developer.android.com/training/basics/activity-lifecycle/recreating.html。 - Stu Whyte
1
我知道,我已经保存了活动状态,但这不是销毁的问题,一切都消失了,包括视图,我的意思是在设备旋转改变时(没有异常情况),我面临着白屏问题。 - Heysem Katibi
你能把你的代码粘贴到发生这种情况的活动中吗? - Stu Whyte
好的,我会更新我的问题。 - Heysem Katibi
在您的MainAcytivity.java onCreate()中,屏幕旋转后是否进入if语句? - Stu Whyte
显示剩余7条评论
8个回答

21

看起来你的主要活动正在使用getSupportFragmentManager(),而你的片段正在使用getFragmentManager()。

不确定这是否值得发布答案,但我的评分太低了,无法以其他方式回复。:)

编辑:我认为你还需要使用支持库扩展FragmentActivity。

请参见:https://dev59.com/dmkv5IYBdhLWcg3wbgMx#10609839

编辑2:

public class MainActivityFragment extends FragmentActivity {
    private ViewPager mPager = null;
    private PagerAdapter mPageAdapter = null;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        final View reVal = inflater.inflate(R.layout.activity_main, container, false);
        mPager = (ViewPager) reVal.findViewById(R.id.pagerMainContent);
        mPageAdapter = new ScreenSlidePagerAdapter(getChildFragmentManager());
        mPager.setAdapter(mPageAdapter);
        return reVal;
    }

    private MainGridViewFragment mainGridFragment;
    private AlphabetGridViewFragment alphabetGridFragment;

    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
        public ScreenSlidePagerAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            switch (position) {
                case 1:
                    mainGridFragment = new MainGridViewFragment();
                    return (Fragment)mainGridFragment;
                case 0:
                    alphabetGridFragment = new AlphabetGridViewFragment();
                    return (Fragment)alphabetGridFragment;
                default:
                    return null;
            }
        }

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

同时使用两个Fragment Managers是正常的吗?我认为这样做会得到两个不同的对象。因此,当您创建一个新的Activity时,第一个Fragment将使用support manager,而第二个非support manager将会丢失。 - Jon
我正在使用 getFragmentManager(),因为它返回 android.app.FragmentManager,而 getSupportFragmentManager() 返回 android.support.v4.app.FragmentManager,这意味着它们不返回相同的对象。那我应该使用哪个? - Heysem Katibi
我正在使用ICS,但是ViewPager仅存在于支持库中。 - Heysem Katibi
我已经修改了你上面的代码,使用了这个库。你可能需要调整你项目中的其余部分以匹配和清理/重建你的项目。 - Jon
6
getChildFragmentManager()似乎有助于解决我类似的问题。 - Truong Nguyen
显示剩余4条评论

9

我尝试了很多解决方案,但都没有成功。最后我修复如下:

  • Use getActivity().getSupportFragmentManager() rather than getChildFragmentManager()

  • Override getItemId(int position) method in your ViewPagerAdapter class like below:

    @Override
    public long getItemId(int position) {
        return System.currentTimeMillis();
    }
    
对我来说它的工作效果很好,也请在这里发表你的看法。

2
重写 getItemId 对我有很大帮助。我从来没有想到过。我猜 ViewPager 必须根据 ID 缓存片段。无论如何,谢谢。 - steve
如果任何人无法覆盖getItemId(),请使用FragmentPagerAdapter而不是FragmentStatePagerAdapter - NJY404

5
对于那些遇到问题的人,您可以尝试以下方法:

private void InitializeUI(){
mSectionsPagerAdapter = new SectionsPagerAdapter(getSupportFragmentManager());
mViewPager = (ViewPager) findViewById(R.id.container);
mViewPager.setAdapter(mSectionsPagerAdapter);

}

@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.activity_exame2);
InitializeUI();
}

我花了两天时间解决这个问题,以下是我找到的唯一有效的方法:请记得在清单文件中为您的活动添加“android:configChanges = screenSize | orientation”属性。

3
尽量避免重复创建viewpager。将onCreateView中的代码放在onCreate()中,让你的fragment使用onRetainInstance(true)。这样就不会重新创建fragment了,而且因为view在onCreate()中,所以它不会再次被调用。
在onCreateView()中检查视图是否存在,如果存在,则删除它。 代码示例:
//creates the view
private View createView(Bundle savedInstanceState) {
    View view = getActivity().getLayoutInflater().inflate(your_layout, null, false);

    //viewpager

    return view;
}

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

    //retains fragment instance across Activity re-creation
    setRetainInstance(true);

    viewRoot = createView(savedInstanceState);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {

    if (viewRoot != null && viewRoot.getParent() != null) {
        ViewGroup parent = (ViewGroup) viewRoot.getParent();
        parent.removeView(viewRoot);

        Log.i("Test", "**************view.getParent(): " + viewRoot.getParent() + "    " + viewRoot);
    }

    return viewRoot;
}

我不知道这是否有帮助,但是请尝试一下。在此期间,我会继续解决您的问题。 - android developer

3

我曾经遇到过这个问题。这真的很困惑,但是这个线程帮助了我,尽管答案并没有真正解释为什么它起作用。

解决这个问题的方法是使用Fragment.getChildFragmentManager()。让人困惑的是,Android API有Activity.getFragmentManager(),但是如果你想支持旧设备,你需要使用Activity.getSupportFragmentManager()。然而,这个特定的问题不是关于支持旧API的。

问题是你有一个Activity,其中包含一个Fragment,该Fragment拥有一个ViewPager,其中包含多个Fragments。你有一个嵌套的Fragment!最初设计Fragments时并没有真正处理这种情况,并在后来进行了更新以处理它。当你有嵌套的Fragments时,你需要使用:

Fragment.getChildFragmentManager()

在一个Fragment中,你可以选择使用Fragment.getFragmentManager(),它返回ActivityFragmentManager,或者使用Fragment.getChildFragmentManager(),它返回此FragmentFragmentManager,适当地限定了你的Fragment中的Fragment的范围。


1

你应该使用 getChildFragmentManager() 而不是 getFragmentManager()。 你使用了

mPageAdapter = new ScreenSlidePagerAdapter(getActivity().getFragmentManager());

它本应该是。
mPageAdapter = new ScreenSlidePagerAdapter(getActivity().getChildFragmentManager());

在您的代码片段中。

1
这其实是正确的,除了getAcitivity().getChildFragmentManager()不存在。它不应该被投反对票。 - chubbsondubs
你应该像这样使用它: pagerAdapter = new PagerAdapter(getChildFragmentManager()); - AlexS

1
在我的情况下,有一个名为A的活动,其中包含一个名为F1的片段,该片段具有视图页V1,该视图页具有片段F2,该片段具有视图页V2,该视图页具有片段F3。:)
当旋转最后一个片段F3时,会出现空白或错误。
我的解决方案是:
  1. 删除PageChangeListener,基本上在更改片段时不使用片段事务,而是保留默认设置。
  2. 使用FragmentStatePagerAdapter代替FragmentPagerAdapter
  3. 在适配器中重写getItemPosition()并返回POSITION_NONE
  4. 在适配器中创建一个带有片段列表的字段,并在构造函数中设置它
  5. 在适配器中的getItem()中只返回fragmentList.get(position)
  6. 在适配器中的getCount()中只返回fragmentList.size()
  7. 不要覆盖适配器中的任何其他方法,例如destroy,instantiate等
  8. 在F2的onCreateView()中创建和实例化V2适配器
编辑
例如,当需要将F1中的V1替换为F2中的V2时:
  1. 使用相对布局将V1封装在id为F1_container的容器中
  2. 使用getSupportFragmentManager()并开始事务
  3. 删除F1
  4. 用F2替换F1_container
  5. 添加到后退栈
  6. 提交事务

稍后需要从F2返回F1时,在有效的A引用上调用popBackStack()。

我想这就是全部了,这个过程花费了一天以上的时间,每次更改都要重新启动应用程序并查看结果。希望这能有所帮助。


0

我一直在使用数据绑定,同时让片段A托管viewPager,到目前为止旋转不是问题,但是导航到片段B并返回,然后旋转就成了问题。结果发现我分配的是parentFragmentManager而不是childFragmentManager。这是Android开发的基本原理,我很尴尬忘记了这个 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接