Почему фрагмент не сохраняет состояние при повороте экрана?

у меня возникли проблемы с получением некоторых пользовательских подклассов DialogPreference внутри PreferenceFragment, чтобы оставаться видимым при повороте экрана. Я не испытываю эту проблему при использовании PreferenceActivity, поэтому я не знаю, является ли это ошибкой Android или проблемой с моим кодом, но я хотел бы, чтобы кто-то подтвердил, имеют ли они тот же опыт.

чтобы проверить это, сначала создайте экран предпочтений, содержащий хотя бы один DialogPreference (он не вопрос какой подкласс). Затем отобразите его в PreferenceActivity. При запуске приложения нажмите на DialogPreference, чтобы он отображался в диалоговом окне. Затем поверните экран так, чтобы ориентация изменилась. Диалог остается видимым?

затем попробуйте то же самое, но с PreferenceFragment для отображения ваших предпочтений вместо PreferenceActivity. Опять же, диалог остается видимым при повороте экрана?

до сих пор, я обнаружил, что окно останется видимым, если использование PreferenceActivity, но не при использовании PreferenceFragment. Глядя на исходный код для DialogPreference, кажется, что правильное поведение для диалога остается видимым, потому что isDialogShowing - Это информация о состоянии, которая сохраняется, когда onSaveInstanceState() вызывается при переориентации экрана. Поэтому я думаю, что ошибка может помешать PreferenceFragment (и всему внутри него) восстановить эту информацию о состоянии.

Если это ошибка Android, то это имеет далеко идущие последствия, потому что любой, кто использует PreferenceFragment не может сохранять и восстанавливать информацию о состоянии.

кто-нибудь может подтвердить? Если это не жук, тогда что происходит?

2 ответов


наконец-то нашел решение этой проблемы. Оказывается, это не ошибка, а проблема/надзор в документации разработчика Android.

вы видите, я следовал учебнику PreferenceFragment здесь. Эта статья говорит вам сделать следующее, чтобы создать экземпляр 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() (показано выше), который вызовет onActivityCreated() метод для вашего PreferenceFragment () и onRestoreInstanceState() метод для каждого предпочтения, которые он содержит. Они успешно восстановят состояние всего.

но тогда один раз, что вызов super.onCreate() возвращает, вы можете видеть, что onCreate() затем метод продолжит создание PreferenceFragment a второй времени. Потому что он бессмысленно создан снова (и на этот раз без государственной информации!), все состояние, которое было только что успешно восстановлено, будет полностью отброшено / потеряно. Это объясняет, почему DialogPreference, который может отображаться в момент уничтожения действия, больше не будет виден после повторного создания действия.

Итак, каково решение? Ну, просто добавьте небольшую проверку, чтобы определить, был ли 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() имеет двойную роль - он может настроить действие в первый раз, или он может восстановить из более раннего состояния.

ответ здесь привело меня к осознанию этого решение.


у меня действительно была эта проблема. Существует ошибка, где 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 параметр, возможно, вам придется вызвать super с WorkaroundSavedState.savedInstanceState. И когда вы восстанавливаете состояние (т. е. в onCreate(), просто игнорировать savedInstanceState и вместо этого используйте WorkaroundSavedState.savedInstanceState. Статический держатель не самое чистое решение, но оно работает. Просто убедитесь, что в вашем onDestroy().

во всяком случае, мой DialogFragment не исчезает, когда я поворачиваю экран (и это без каких-либо configChanges). Дайте мне знать, если этот код решает вашу проблему, и если нет, я посмотрю, что происходит. Также обратите внимание, что я не тестировал это в PreferenceFragment но вместо других Fragments из класса совместимости или из ActionBarSherlock.