片段,DialogFragment和屏幕旋转

时间:2011-11-22 22:55:34

标签: android android-fragments

我有一个用这个XML调用setContentView的Activity:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="horizontal"
    >
    <fragment android:name="org.vt.indiatab.GroupFragment"
        android:id="@+id/home_groups"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_weight="1" />
            <..some other fragments ...>
</LinearLayout>

GroupFragment扩展了Fragment,一切都很顺利。但是,我在GroupFragment中显示了一个DialogFragment。这显示正确,但是当屏幕旋转时,我得到一个强制关闭。

从DialogFragment.show(FragmentManager,String)以外的其他片段中显示DialogFragment的正确方法是什么?

9 个答案:

答案 0 :(得分:54)

兼容性库中存在可能导致此问题的错误。试着把它放在对话片段中:

@Override
public void onDestroyView() {
  if (getDialog() != null && getRetainInstance())
    getDialog().setOnDismissListener(null);
  super.onDestroyView();
}

我还建议将您的dialogfragment设置为保留,因此在轮换后不会被解除。把“setRetainInstance(true);”例如在onCreate()方法中。

答案 1 :(得分:11)

好的,虽然Zsombor的方法有效,但这是由于我对Fragments缺乏经验而且他的解决方案导致了saveInstanceState Bundle的问题。

显然(至少对于DialogFragment),它应该是public static class。您还必须编写自己的static DialogFragment newInstance()方法。这是因为Fragment类在其newInstance方法中调用instantiate()方法。

总而言之,你必须像这样编写DialogFragments:

public static class MyDialogFragment extends DialogFragment {

    static MyDialogFragment newInstance() {
        MyDialogFragment d = new MyDialogFragment();
        return d;
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        ...
    }
}

并向他们展示:

private void showMyDialog() {
    MyDialogFragment d = MyDialogFragment.newInstance();
    d.show(getFragmentManager(), "dialog");
}

这可能是ActionBarSherlock库所独有的,但SDK文档中的官方示例也使用此范例。

答案 2 :(得分:2)

要克服Bundle始终为空,我将其保存到onSaveInstanceState中的静态字段。这是代码味道,但我找到了恢复对话框和保存状态的唯一解决方案。

Bundle引用应该在onDestroy中隐藏。

@Override
public void onCreate(Bundle savedInstanceState)
{
    if (savedInstanceState == null)
        savedInstanceState = HackishSavedState.savedInstanceState;

    setRetainInstance(true);
}

@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
    if (savedInstanceState == null)
        savedInstanceState = HackishSavedState.savedInstanceState;

    ...
}

@Override
public void onDestroyView() // necessary for restoring the dialog
{
    if (getDialog() != null && getRetainInstance())
        getDialog().setOnDismissListener(null);

    super.onDestroyView();
}

@Override
public void onSaveInstanceState(Bundle outState)
{
    ...

    HackishSavedState.savedInstanceState = outState;
    super.onSaveInstanceState(outState);
}

@Override
public void onDestroy()
{
    HackishSavedState.savedInstanceState = null;
    super.onDestroy();
}

private static class HackishSavedState
{
    static Bundle savedInstanceState;
}

答案 3 :(得分:1)

我使用了所提出的解决方案的混合,并添加了一件事。 这是我的最终解决方案:

我在onCreateDialog中使用了setRetainInstance(true); 我用过这个:

public void onDestroyView() {
    if (getDialog() != null && getRetainInstance())
        getDialog().setDismissMessage(null);
    super.onDestroyView();
}

作为savedInstanceState无法解决的解决方法,我创建了一个名为StateHolder的私有类(与为listView创建持有者的方式相同):

private class StateHolder {
    String name;
    String quantity;
}

我以这种方式保存状态:

@Override
public void onSaveInstanceState(Bundle savedInstanceState) {
    super.onSaveInstanceState(savedInstanceState);
    stateHolder = new StateHolder();
    stateHolder.name = actvProductName.getText().toString();
    stateHolder.quantity = etProductQuantity.getText().toString();
}

在onDismiss方法中,我将stateHolder设置为null。创建对话框时,它会验证stateHolder是否为null以恢复状态或只是正常初始化所有内容。

答案 4 :(得分:1)

我用@ZsomborErdődy-Nagy和@AndyDennie的答案解决了这个问题。您必须将此类以及父级片段调用setRetainInstance(true)dialogFragment.show(getFragmentManager(), "Dialog");

子类化
 public class AbstractDialogFragment extends DialogFragment {

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

        @Override
        public void onDestroyView() {
            if (getDialog() != null && getRetainInstance())
                getDialog().setDismissMessage(null);
            super.onDestroyView();
        }
    }

答案 5 :(得分:0)

我有一个类似的问题,但上述都没有对我有用。最后,我需要在代码中而不是在XML布局中创建片段。

请参阅:Replacing fragments and orientation change

答案 6 :(得分:0)

我在我的项目中碰到了这个,上述解决方案都没有帮助。

如果异常看起来像


java.lang.RuntimeException: Unable to start activity ComponentInfo{ 

...

        Caused by: java.lang.IllegalStateException: Fragment.... 
        did not create a view.

这是由轮回后使用的后备容器ID的问题引起的。有关详细信息,请参阅此票证:

https://code.google.com/p/android/issues/detail?id=18529

基本上,您可以通过确保所有xml片段都在布局中定义了标记来防止崩溃。如果在片段可见时旋转,则可以防止发生回退情况。

在我的情况下,我能够应用此修复,而必须覆盖onDestroyView()或setRetainInstance(true),这是这种情况的常见建议。

答案 7 :(得分:0)

我遇到了这个问题而且onDestroyView()伎俩并没有奏效。事实证明,这是因为我在onCreate()中进行了一些相当密集的对话创建。这包括保存对AlertDialog的引用,然后我将在onCreateDialog()中返回。

当我将所有这些代码移到onCreateDialog()并停止保留对该对话框的引用时,它又开始工作了。我希望我违反了其中一个不变量DialogFragment关于管理其对话框的内容。

答案 8 :(得分:0)

onCreate()致电setRetainInstance(true),然后加入:

@Override
public void onDestroyView() {
    if (getDialog() != null && getRetainInstance()) {
        getDialog().setOnDismissMessage(null);
    }
    super.onDestroyView();
}

当您在onCreate()中调用setRetainInstance(true)时,将不再调整onCreate()方向更改,但仍会调用onCreateView()。

因此,您仍然可以将状态保存到onSaveInstanceState()的捆绑包中,然后在onCreateView()中检索它:

@Override
public void onSaveInstanceState(Bundle outState) {

    super.onSaveInstanceState(outState);

    outState.putInt("myInt", myInt);
}

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

    View view = inflater.inflate(R.layout.my_layout, container);

    if (savedInstanceState != null) {

        myInt = savedInstanceState.getInt("myInt");
    }

    ...

    return view;
}