Подтвердить что ты не робот

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

Вы видите, я был следующий учебник PreferenceFragment здесь. В этой статье вам предлагается сделать следующее, чтобы создать экземпляр вашего PreferenceFragment в рамках Activity:

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 будет создан дважды, что и заставляет его потерять свое состояние.

Первое создание произойдет через вызов Activity для super.onCreate() (показано выше), который вызовет метод onActivityCreated() для вашего метода PreferenceFragment() и onRestoreInstanceState() для каждого Предпочтения, которое он содержит. Они успешно восстановят состояние всего.

Но когда - то, что вызов super.onCreate() возвращает, вы можете увидеть, что onCreate() метод будет продолжаться, чтобы создать PreferenceFragment во второй раз. Поскольку это бессмысленно создано снова (и на этот раз, без информации о состоянии!), Все состояние, которое было просто успешно восстановлено, будет полностью отброшено/потеряно. Это объясняет, почему 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() имеет двойную роль - он может настроить Activity в первый раз или восстановить его из более раннего состояния.

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

Ответ 2

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

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