2011-11-22 98 views
24

我有这个XML调用的setContentView的活动:片段,DialogFragment,以及屏幕旋转

<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延伸片段,一切都很好那里。但是,我在GroupFragment中显示了一个DialogFragment。这显示正确,但是当屏幕旋转时,我得到一个强制关闭。

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

+0

你能提供力闭合的堆栈跟踪? – NPike

+0

你可以发布实例化DialogFragment的代码行吗? –

回答

11

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

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

所以在最后,你必须写你的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文档也使用这种范例。

+0

我试过让这种方法工作,但仍然出现错误。源代码是https://github.com/gauntface/Android-Boiler-Plate-Kitchen-Sink [.activity.DialogActivity] –

+15

您不必将“自己的”newInstance()方法写为Fragment.instantiate()正在调用[Class.newInstance()](http://developer.android.com/reference/java/lang/Class.html#newInstance%28%29)。但是因为您的片段实例可能使用Class.newInstance()实例化,所以您必须为您编写的每个片段提供一个显式的默认构造函数。 –

+0

@SzabolcsBerecz:好的哇,只是增加了如此多的清晰度。非常感谢。 – Weston

54

兼容库中存在导致此问题的错误。尝试把这个在你dialogfragment:

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

我也建议设置你dialogfragment留存,所以旋转后它不会被解雇。把“setRetainInstance(true);”例如在onCreate()方法中。

+10

setRetainInstance(true)修复崩溃,但对话框在方向更改中被简单地解除。同时拥有setRetainInstance(true)和上面的代码片段将导致它重新显示。唯一的问题是,不知怎的,savedInstanceState包越来越混乱。改变方向后,我无法将我的数值恢复。当调用onCreateDialog()时,Bundle始终为空。有任何想法吗? – Weston

+1

这解决了我的任务来创建持久性对话框。使用V4兼容性库。但是您确定使用本地API 11 DialogFragment不需要此解决方法吗? –

+1

我没有看它,因为我只使用backport库。我不会感到惊讶,如果这仍然是api level 11中的bug。我建议使用backport lib来处理ICS下面的任何东西,这样可以确保片段框架始终如一地工作。 –

2

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

该软件包的引用应在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; 
} 
+0

只要该类的类加载器处于活动状态,此静态成员就会留在内存中,这将需要一些时间... – Eugene

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,以恢复状态或仅正常初始化所有内容。

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),这是这种情况的常见建议。

0

我遇到了这个问题,并且onDestroyView()技巧不起作用。事实证明,这是因为我在onCreate()中做了一些相当密集的对话创建。这包括保存对AlertDialog的引用,然后我将在onCreateDialog()中返回。

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

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

我注意到,如果应用程序显示另一个对话框片段,例如googleplayservices应用程序崩溃方向更改。 –

0

onCreate()通话setRetainInstance(true)然后包括此:

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

当你的onCreate()的onCreate调用setRetainInstance(true)()将不再调用方向更改,但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; 
}