我在PreferenceFragment中获取一些自定义DialogPreference子类时遇到一些麻烦,以便在旋转屏幕时保持可见。我在使用PreferenceActivity时没有遇到这个问题,因此我不知道它是Android错误还是代码问题,但我希望有人确认他们是否有相同的体验。
要测试这一点,首先要创建一个包含至少一个DialogPreference的首选项屏幕(哪个子类无关紧要)。然后在PreferenceActivity中显示它。运行应用程序时,请按DialogPreference以显示其对话框。然后旋转屏幕以使方向发生变化。对话框是否仍然可见?
然后尝试相同,但使用PreferenceFragment显示您的首选项而不是PreferenceActivity。同样,旋转屏幕时对话框是否仍然可见?
到目前为止,我发现如果使用PreferenceActivity,对话框仍然可见,但如果使用PreferenceFragment则不会。查看source code for DialogPreference,似乎正确的行为是让对话框保持可见,因为isDialogShowing
是在屏幕重定向时调用onSaveInstanceState()
时保存的状态信息。因此,我认为一个错误可能会阻止PreferenceFragment(及其中的所有内容)恢复该状态信息。
如果是Android错误,则会产生深远的影响,因为使用PreferenceFragment的任何人都无法保存和恢复状态信息。
有人可以确认一下吗?如果它不是一个错误,那么发生了什么?
答案 0 :(得分:51)
最后找到了解决这个问题的方法。事实证明,这不是一个错误,而是Android开发人员文档中的问题/疏忽。
你知道,我正在关注PreferenceFragment教程here。该文章告诉您执行以下操作以在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();
}
}
这样做的问题是,当您更改屏幕方向(或任何其他破坏并重新创建活动的操作)时,您的PreferenceFragment将被创建两次,这是导致它的原因失去它的状态。
第一次创建将通过活动调用super.onCreate()
(如上所示)进行,该调用将为您的PreferenceFragment()和{{1}调用onActivityCreated()
方法它包含的每个首选项的方法。这些将成功恢复一切状态。
然后,一旦调用onRestoreInstanceState()
,您就可以看到super.onCreate()
方法将继续创建PreferenceFragment second 时间。因为它是无意义地再次创建(这次,没有状态信息!),刚刚成功恢复的所有状态将被完全丢弃/丢失。这解释了为什么在重新创建活动后,可能在销毁活动时显示的DialogPreference将不再可见。
那么解决方案是什么?好吧,只需添加一个小的检查来确定是否已经创建了PreferenceFragment,如下所示:
onCreate()
或者另一种方法是简单地检查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();
}
}
}
具有双重角色 - 它可以首次设置一个活动,或者它可以从之前的状态恢复。
答案here促使我实现了这个解决方案。
答案 1 :(得分:0)
我自己确实遇到过这个问题。有一个错误,DialogFragment
没有恢复状态,因为它是null,或者至少它发生在我身上。
使用多个来源我最终得到了一个解决方案。让您的对话框扩展此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
。静态持有者不是最干净的解决方案,但它确实有效。只需确保在onDestroy()
中将其设置为空。
在任何情况下,当我旋转屏幕时,我的DialogFragment
不会消失(而且没有任何configChanges
)。如果此代码解决了您的问题,请告诉我,如果没有,我会看看发生了什么。另请注意,我尚未在PreferenceFragment
中对此进行测试,而是在兼容性类或Fragment
中测试了其他ActionBarSherlock
。