29

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

为了测试这一点,首先创建一个包含至少一个 DialogPreference 的首选项屏幕(哪个子类都没有关系)。然后在 PreferenceActivity 中显示它。当您运行您的应用程序时,按下 DialogPreference 以显示它的对话框。然后旋转屏幕以改变方向。对话框是否仍然可见?

然后尝试相同的方法,但使用 PreferenceFragment 来显示您的偏好,而不是 PreferenceActivity。同样,当您旋转屏幕时对话框是否仍然可见?

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

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

有人可以确认吗?如果它不是一个错误,那么发生了什么?

4

2 回答 2

56

终于想出了解决这个问题的办法。事实证明这不是错误,而是 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();
    }
} 

这样做的问题是,当您更改屏幕方向(或任何其他破坏并重新创建 Activity 的操作)时,您的 PreferenceFragment 将被创建两次,这就是导致它失去其状态的原因。

一次创建将通过 Activity 的调用发生super.onCreate()(如上所示),它将调用onActivityCreated()PreferenceFragment () 的onRestoreInstanceState()方法及其包含的每个 Preference 的方法。这些将成功恢复一切的状态。

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

那么解决方案是什么?好吧,只需添加一个小检查以确定 PreferenceFragment 是否已创建,如下所示:

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()有双重作用——它可以第一次设置一个活动,或者它可以从早期的状态恢复。

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

于 2013-01-13T13:36:54.507 回答
0

我自己确实有这个问题。有一个错误,DialogFragment因为它是空的,所以它不会恢复状态,或者至少它发生在我身上。

使用多个来源,我最终得到了一个可行的解决方案。让你的对话框扩展这个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参数的任何子类中,您可能必须使用 super 调用WorkaroundSavedState.savedInstanceState。当您恢复状态时(即在 中onCreate(),只需忽略savedInstanceState并使用WorkaroundSavedState.savedInstanceState。静态支架不是最干净的解决方案,但它可以工作。只需确保在您的onDestroy().

无论如何,DialogFragment当我旋转屏幕时 my 不会消失(而且没有任何configChanges)。让我知道这段代码是否解决了您的问题,如果没有,我会看看发生了什么。另请注意,我没有在其中测试过这个,PreferenceFragment而是Fragment在兼容性类或来自ActionBarSherlock.

于 2013-01-10T05:37:54.440 回答