屏幕旋转后,碎片无法正常工作

14

我在使用Fragments和屏幕旋转时遇到了问题。

我的应用有一个MainActivity,通过选项卡式操作栏来切换Fragments。以下是选项卡式操作栏的代码以及当选项卡被选择时的处理方式:

private class MyTabListener <T extends android.support.v4.app.Fragment> implements ActionBar.TabListener {
    private android.support.v4.app.Fragment mFragment;
    private final Activity mActivity;
    private final String mTag;
    private final Class<T> mClass;

    public MyTabListener(SherlockFragmentActivity activity, String tag, Class<T> clz) {
        mActivity = activity;
        mTag = tag;
        mClass = clz;
    }

    @Override
    public void onTabSelected(Tab tab, android.support.v4.app.FragmentTransaction ft) {
        if (mFragment == null){ // check to see if the fragment has already been initialised. If not create a new one.
            mFragment = android.support.v4.app.Fragment.instantiate(mActivity, mClass.getName());
            ft.add(android.R.id.content,mFragment,mTag);
        } else {
            ft.attach(mFragment); // if the fragment has been initialised attach it to the current activity
        }
    }

    @Override
    public void onTabUnselected(Tab tab, android.support.v4.app.FragmentTransaction ft) {
        if (mFragment != null){
            ft.detach(mFragment); // when a fragment is no longer needed, detach it from the activity but dont destroy it
        }
    }

    @Override
    public void onTabReselected(Tab tab, android.support.v4.app.FragmentTransaction ft) {

    }

我认为,在应用程序首次以纵向模式加载时,一切都正常运作。但是当我旋转设备时,某种原因会导致另一个 Fragment1 的实例被添加,并且在选择另一个选项卡后不会被分离。当我旋转回纵向模式时,情况也是如此。
我尝试在我的Fragments中使用setRetainInstance(true);,但这没有起作用。
在旋转之前,我需要重写某个方法或对我的Fragments进行某些操作吗?
提前感谢您的帮助。
编辑:我现在知道当活动重新创建时,会再次调用 onTabSelected。这可能是我遇到的关于片段被多次连接的问题的原因吗?
这是我的活动onCreate方法。
super.onCreate(savedInstanceState);

        if (savedInstanceState == null) {
           //setContentView(R.layout.main);

        }
     // Create the Action Bar with tabs
        ActionBar actionBar = getSupportActionBar();
        actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
        actionBar.setDisplayShowTitleEnabled(false);
        //create the tab for track and add it to the action bar.
        Tab tab = actionBar.newTab()
                            .setText("Track")
                            .setTabListener(new MyTabListener<TrackingFragment>(this,"track",TrackingFragment.class));
        actionBar.addTab(tab);

        //create the tab for ski tracks and add it to the action bar.
        tab = actionBar.newTab()
                        .setText("Something Tracks")
                        .setTabListener(new MyTabListener<TrackListFragment>(this,"somethingtracks",TrackListFragment.class));
        actionBar.addTab(tab);

        //create the tab for photos and add it to the action bar.
        tab = actionBar.newTab()
                        .setText("Photos")
                        .setTabListener(new MyTabListener<PhotoFragment>(this,"photos",PhotoFragment.class));
        actionBar.addTab(tab);
5个回答

16

简短修复:

onTabSelected 方法中,在使用 if (mFragment == null) 之前,您需要尝试获取片段(使用 mFragment = getSupportFragmentManager().findFragmentByTag(mTag))。您还可以从外部设置此选项,但我没有看到您这样做。

onCreate 上检查 if(savedInstanceState == null) 也可以解决此问题,我认为这是更好的方法! :)


如果我将我的操作栏代码放在onCreate方法中,当活动在方向更改后重新创建时,它不会被创建或显示。我现在将尝试使用supportManager部分。 - StuStirling
+1...在我编辑时悄悄地插进来了正确的答案。 :) - Barak
当调用setTabListener时,该片段是否未被传递? - StuStirling
看一下构造函数。public MyTabListener(SherlockFragmentActivity activity, String tag, Class<T> clz)... 一个活动、一个标签和一个类。没有传入任何片段。 - Barak
谢谢@Barak和Pedro,你们都帮了我很多。如果我能给你们两个绿色的勾就好了。最后只是从管理器中获取片段并进行检查。就像你们说的那样。 - StuStirling
1
没问题,Pedro 先想到了这个。如果我的贡献对你有帮助,你可以给我的答案点赞,而不必选择它作为被采纳的答案。 - Barak

8
似乎您的onCreate方法中没有包含if(savedInstanceState == null),因此除了从savedInstanceState捆绑恢复的片段外,您还创建了另一个片段。 编辑 仔细查看您的代码,我认为我的关于onCreate的理解是错误的,您的onTabSelected应该处理它。 我认为if (mFragment == null)始终为空,因为您没有尝试找到该片段。 将该代码部分更改为:
@Override 
public void onTabSelected(Tab tab, android.support.v4.app.FragmentTransaction ft) { 

    mFragment = mActivity.getSupportFragmentManager().findFragmentByTag(mTag);   // add this

    if (mFragment == null){ // check to see if the fragment has already been initialised. If not create a new one. 
        mFragment = android.support.v4.app.Fragment.instantiate(mActivity, mClass.getName()); 
        ft.add(android.R.id.content,mFragment,mTag); 
    } else { 
        ft.attach(mFragment); // if the fragment has been initialised attach it to the current activity 
    } 
} 

我们是在我的Fragment还是主Activity中交流? - StuStirling
我猜测你的主要活动是设置并显示初始视图的任何内容。 - Barak
我已经将我的onCreate方法添加到上面的问题中。 - StuStirling
更新了我的回答。我觉得我走错了方向。 - Barak

2

我发现setRetainInstance实际上被忽略了,由android.support.v4.app.FragmentManager创建的所有Fragment都存储在onSaveInstanceState中,并在onCreate中重新创建。

对于我来说,解决方案是直接删除多余保存的Fragment:https://dev59.com/-2ox5IYBdhLWcg3wFAU1#13996054


1

我遇到了类似的问题,但是上面提出的解决方案在我的情况下似乎没有起作用。最终我找到了以下解决方案:

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
    if (mFragment == null) {
        mFragment = Fragment.instantiate(mActivity, mClass.getName());
        ft.replace(android.R.id.content, mFragment, mTag); // Use replace iso add
    }
    else {
        ft.attach(mFragment);
    }
}

0

如果(savedInstanceState == null)并不总是有效。有时,当你需要添加另一个片段以适应横屏模式时,savedInstanceState != null。

另一种方法是测试fm.findFragmentById(R.id.frameLayoutLeft)是否为空,无论方向如何,如果为空,则创建新的片段实例,否则什么也不做。如果在横屏模式下需要第二个片段,则需要首先检查它是否为横屏模式,如果是,则检查fm.findFragmentById(R.id.frameLayoutRight)是否为空。如果为空,则创建,否则什么也不做,因为它已经由Android OS保留。


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