Почему фрагмент не сохраняет состояние при повороте экрана?
у меня возникли проблемы с получением некоторых пользовательских подклассов 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
но вместо других Fragment
s из класса совместимости или из ActionBarSherlock
.