旋转活动时片段重叠

5

我对此感到非常困惑。我有一个带有列表导航的 actionbar。我点击列表以打开两个 fragment,并在同一活动中显示。我基本上使用以下方法替换它们:

public void openFragment(AprilAppsFragment createdFragment){        
    if (createdFragment.getClass().isInstance(getDisplayedFragment()))
        return;

    FragmentManager manager = getSupportFragmentManager();
    FragmentTransaction transaction = manager.beginTransaction();
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);

    transaction.replace( R.id.main_fragment, createdFragment, "displayed fragment"); 
    transaction.addToBackStack(null);
    transaction.commit();  
}

我打开了碎片A,然后打开了碎片B,接着旋转了屏幕。碎片A被重新创建导致我的应用崩溃。
由于我正在使用“replace”,为什么会发生这种情况?如何避免重新创建不再显示的碎片,而不失去返回到它们的可能性?

你能添加崩溃时的堆栈跟踪吗? - ianhanniballake
当片段尝试使用数据填充其内容时,由于尚未显示,因此无法实例化视图组件,导致崩溃发生。 - Jacek Kwiecień
你是从 OnNavigationListener 中调用该方法吗?如果是,请记住该监听器将在活动创建/重新创建时触发。 - user
1
语句 if (createdFragment.getClass().isInstance(getDisplayedFragment())) return; 非常可疑。你能否展示一下堆栈跟踪?堆栈跟踪从来都不是无关紧要的。 - Sherif elKhatib
5个回答

10
您的问题是您没有保存片段的状态,因此您尚未添加
android:configChanges="orientation|screenSize"

你的第一个片段再次被调用,由于没有保存状态,因此应用程序崩溃了。所以很好,你在清单中添加了上面一行代码:

<activity android:name=".yourActivity" android:configChanges="orientation|screenSize"

重写onSaveInstanceState方法,为您的两个片段保存状态。然后在onCreate方法中进行如下检查:

if(savedInstanceState != null){
fragmentA = (FragmentA) fragmentManager.getFragment(savedInstanceState, stringValueA);
fragmentB = (FragmentB) fragmentManager.getFragment(savedInstanceState, stringValueB);
}

然后检查您的任何片段是否不为空,如果是,则创建其实例并将其设置为fragmentTransaction.relplace。或在您的for openFragment方法中执行此操作。

干杯!


1
它并没有真正回答为什么在娱乐活动中的活动会重新创建所有片段,即使那些被替换的片段。configChanges实际上可以阻止活动的重新创建,但它在ActionBarSherlock上无法工作,只有在后期ICS系统上值得一试。 - Jacek Kwiecień
在这个线程中阅读答案:https://dev59.com/3GYq5IYBdhLWcg3wzDrk - AliR
很少有这样令人烦恼的问题可以用一行代码解决,但是就是这样。谢谢@AliR。 - mwieczorek

2

片段在旋转时会自动保存其状态(以及后退栈上的位置)。确保您不会在onActivityCreated或onCreate方法中重新创建片段或调用openFragment。


那么你唯一创建片段A和片段B的位置(即调用此openFragment方法)是在列表的点击方法中吗? - ianhanniballake
只有那一个地方,确切无误。 - Jacek Kwiecień

2
您的问题在于您的Activity正在重新创建,这会导致您需要重新设置列表导航,从而触发默认(第一个)位置。您需要在Activity销毁和重新创建之间保存所选索引。以下是一个示例:
@Override
public void onCreate(Bundle savedInstanceState) {

    // onCreate implementation...

    // Must come after setListNavigationCallbacks is called
    if (savedInstanceState != null) {
        int index = savedInstanceState.getInt("index");
        getActionBar().setSelectedNavigationItem(index);
    }
}

@Override
protected void onSaveInstanceState(Bundle outState) {

    super.onSaveInstanceState(outState);
    outState.putInt("index", getActionBar().getSelectedNavigationIndex());
}

1
我遇到了类似的问题,我的解决方法是在Androidmanifest.xml文件中添加一行代码(在您想要停止重新创建的活动内部):

android:configChanges="orientation"

还有一件事,我了解到片段是一种子活动,而不是活动本身。我们必须扩展一个带有片段的活动,以获得对片段的支持,而扩展活动片段的活动实际上是持有称为片段的“子活动”的实际活动。
我希望能帮助您...

1

将以下内容添加到AndroidManifest.xml文件中的活动定义中:

android:configChanges="keyboardHidden|screenLayout|orientation|screenSize"

这样做可以防止活动重新启动和 FragmentManager 自动重新创建它拥有的片段。
如果您需要在旋转时重启活动,则必须查找传递到 onCreate 中的 savedInstanceState,并使用您提供的标签查询 FragmentManager 获取您的片段。

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