屏幕旋转时为什么不保留片段状态?

我一直有一些麻烦,让一个PreferenceFragment内的一些自定义DialogPreference子类保持可见,当屏幕旋转。 在使用PreferenceActivity的时候,我没有遇到这个问题,所以我不知道这是Android错误还是代码问题,但是我希望有人确认他们是否有相同的体验。

为了testing这个,首先创build一个包含至less一个DialogPreference的首选项屏幕(哪个子类无关紧要)。 然后将其显示在PreferenceActivity中。 当你运行你的应用程序时,请按下DialogPreference,使其显示对话框。 然后旋转屏幕,使方向改变。 对话框是否可见?

然后尝试相同,但PreferenceFragment显示您的首选项,而不是一个PreferenceActivity。 同样,旋转屏幕时,对话框是否仍然可见?

到目前为止,我发现如果使用PreferenceActivity,对话框将保持可见,但如果使用PreferenceFragment则不会。 查看DialogPreference的源代码 ,似乎正确的行为是让对话框保持可见状态,因为isDialogShowing是在屏幕重新定向时调用onSaveInstanceState()时保存的状态信息。 因此,我认为一个错误可能会阻止PreferenceFragment(及其中的所有内容)恢复该状态信息。

如果这是一个Android错误,那么它具有深远的影响,因为使用PreferenceFragment的任何人都无法保存和恢复状态信息。

有人可以确认吗? 如果这不是一个错误,那么是怎么回事?

Solutions Collecting From Web of "屏幕旋转时为什么不保留片段状态?"

最后想出了解决这个问题的办法。 事实certificate,这不是一个错误,而是Android开发人员文档中的一个问题/疏漏。

你看,我在这里按照PreferenceFragment教程。 那篇文章告诉你要执行以下操作来在一个Activity中实例化PreferenceFragment:

 public class SettingsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } } 

这个问题是,当你改变屏幕的方向(或任何其他破坏和重新创buildActivity的动作),你的PreferenceFragment会被创build两次 ,这就是导致它失去状态的原因。

一次创build将通过Activity对super.onCreate() (如上所示)的调用来super.onCreate() ,它将为您的PreferenceFragment()和onRestoreInstanceState()方法为其包含的每个首选项调用onActivityCreated()方法。 这些将成功恢复一切的状态。

但是一旦对super.onCreate()调用返回,就可以看到onCreate()方法将继续再次创buildPreferenceFragment。 因为它没有被无意义地创build(这次没有状态信息!),所有刚被成功恢复的状态将被完全丢弃/丢失。 这就解释了为什么在活动被销毁时可能显示的DialogPreference在活动重新创build后将不再可见。

那么解决scheme是什么? 那么,只需添加一个小的检查,以确定是否已经创buildPreferenceFragment,如下所示:

 public class SettingsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Fragment existingFragment = getFragmentManager().findFragmentById(android.R.id.content); if (existingFragment == null || !existingFragment.getClass().equals(SettingsFragment.class)) { // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } } } 

或者另一种方法是简单地检查onCreate()是否意味着恢复状态,如下所示:

 public class SettingsActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState == null) { // Display the fragment as the main content. getFragmentManager().beginTransaction() .replace(android.R.id.content, new SettingsFragment()) .commit(); } } } 

所以我想这里学到的教训是, onCreate()有双重作用 – 它可以第一次设置一个Activity,或者它可以从早期的状态恢复。

这里的答案让我意识到这个解决scheme。

我自己确实有这个问题。 有一个错误的地方DialogFragment不恢复状态,因为它是空的,至less它发生在我身上。

使用多个来源,我最终得到了一个解决scheme。 让你的对话扩展这个BaseDialogFragment

 import android.app.Dialog; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.support.v4.app.DialogFragment; import com.actionbarsherlock.app.SherlockDialogFragment; public class BaseDialogFragment extends DialogFragment { @Override public void onCreate(Bundle savedInstanceState) { if (savedInstanceState == null || savedInstanceState.isEmpty()) savedInstanceState = WorkaroundSavedState.savedInstanceState; setRetainInstance(true); Log.d("TAG", "saved instance state oncreate: " + WorkaroundSavedState.savedInstanceState); super.onCreate(savedInstanceState); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { if (savedInstanceState == null || savedInstanceState.isEmpty()) savedInstanceState = WorkaroundSavedState.savedInstanceState; Log.d("TAG", "saved instance state oncretaedialog: " + WorkaroundSavedState.savedInstanceState); return super.onCreateDialog(savedInstanceState); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (savedInstanceState == null || savedInstanceState.isEmpty()) savedInstanceState = WorkaroundSavedState.savedInstanceState; Log.d("TAG", "saved instance state oncretaeview: " + WorkaroundSavedState.savedInstanceState); return super.onCreateView(inflater, container, 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) { // ... super.onSaveInstanceState(outState); WorkaroundSavedState.savedInstanceState = outState; Log.d("TAG", "saved instance state onsaveins: " + WorkaroundSavedState.savedInstanceState); } @Override public void onDestroy() { WorkaroundSavedState.savedInstanceState = null; super.onDestroy(); } /** * Static class that stores the state of the task across orientation * changes. There is a bug in the compatibility library, at least as of the * 4th revision, that causes the save state to be null in the dialog's * onRestoreInstanceState. */ public static final class WorkaroundSavedState { public static Bundle savedInstanceState; } } 

请注意,在其方法有savedInstanceState参数的任何子类中,可能必须使用WorkaroundSavedState.savedInstanceState调用super。 而当你恢复状态(即在onCreate() ,只是忽略savedInstanceState ,而是使用WorkaroundSavedState.savedInstanceState 。静态持有人不是最干净的解决scheme,但它的工作。只要确保将其设置为null onDestroy()

在任何情况下,我的DialogFragment不会消失,当我旋转屏幕(这是没有任何configChanges )。 让我知道如果这个代码解决您的问题,如果没有,我会看看发生了什么事。 另外请注意,我没有在PreferenceFragmenttesting过,而是从兼容性类或ActionBarSherlock取出了其他的Fragment