片段,DialogFragment,以及屏幕旋转
我有这个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的正确方法是什么?
好的,虽然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文档也使用这种范例。
我试过让这种方法工作,但仍然出现错误。源代码是https://github.com/gauntface/Android-Boiler-Plate-Kitchen-Sink [.activity.DialogActivity] –
您不必将“自己的”newInstance()方法写为Fragment.instantiate()正在调用[Class.newInstance()](http://developer.android.com/reference/java/lang/Class.html#newInstance%28%29)。但是因为您的片段实例可能使用Class.newInstance()实例化,所以您必须为您编写的每个片段提供一个显式的默认构造函数。 –
@SzabolcsBerecz:好的哇,只是增加了如此多的清晰度。非常感谢。 – Weston
兼容库中存在导致此问题的错误。尝试把这个在你dialogfragment:
@Override
public void onDestroyView() {
if (getDialog() != null && getRetainInstance())
getDialog().setOnDismissListener(null);
super.onDestroyView();
}
我也建议设置你dialogfragment留存,所以旋转后它不会被解雇。把“setRetainInstance(true);”例如在onCreate()方法中。
setRetainInstance(true)修复崩溃,但对话框在方向更改中被简单地解除。同时拥有setRetainInstance(true)和上面的代码片段将导致它重新显示。唯一的问题是,不知怎的,savedInstanceState包越来越混乱。改变方向后,我无法将我的数值恢复。当调用onCreateDialog()时,Bundle始终为空。有任何想法吗? – Weston
这解决了我的任务来创建持久性对话框。使用V4兼容性库。但是您确定使用本地API 11 DialogFragment不需要此解决方法吗? –
我没有看它,因为我只使用backport库。我不会感到惊讶,如果这仍然是api level 11中的bug。我建议使用backport lib来处理ICS下面的任何东西,这样可以确保片段框架始终如一地工作。 –
为了克服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;
}
只要该类的类加载器处于活动状态,此静态成员就会留在内存中,这将需要一些时间... – Eugene
我用了所提出的解决方案的组合,并增加了一件事。 这是我的最终解决方案:
我在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,以恢复状态或仅正常初始化所有内容。
我在我的项目中遇到了这个问题,上述解决方案都没有帮助。
如果异常看起来像
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),这是这种情况的常见建议。
我遇到了这个问题,并且onDestroyView()
技巧不起作用。事实证明,这是因为我在onCreate()
中做了一些相当密集的对话创建。这包括保存对AlertDialog
的引用,然后我将在onCreateDialog()
中返回。
当我将所有这些代码移动到onCreateDialog()
并停止保留对该对话框的引用时,它再次开始工作。我期望我违反了其中一个不变式DialogFragment
关于管理其对话。
我用@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();
}
}
我注意到,如果应用程序显示另一个对话框片段,例如googleplayservices应用程序崩溃方向更改。 –
在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;
}
你能提供力闭合的堆栈跟踪? – NPike
你可以发布实例化DialogFragment的代码行吗? –