Фрагменты, Диалоговое Фрагмент и Вращение экрана

У меня есть Activity, который вызывает setContentView с этим XML:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="horizontal" > <fragment android:name="org.vt.indiatab.GroupFragment" android:id="@+id/home_groups" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_weight="1" /> <..some other fragments ...> </LinearLayout> 

GroupFragment расширяет фрагмент, и все хорошо. Тем не менее, я показываю DialogFragment из GroupFragment. Это показывает правильно, ОДНАКО, когда экран вращается, я получаю Force Close.

Каким образом можно отобразить DialogFragment из другого фрагмента, кроме DialogFragment.show (FragmentManager, String)?

Solutions Collecting From Web of "Фрагменты, Диалоговое Фрагмент и Вращение экрана"

В библиотеке совместимости есть ошибка, которая может вызвать это. Попробуйте поставить это в диалоговом окне:

 @Override public void onDestroyView() { if (getDialog() != null && getRetainInstance()) getDialog().setOnDismissListener(null); super.onDestroyView(); } 

Я также предлагаю установить ваш диалог, как сохраненный, поэтому он не будет уволен после поворота. Поместите «setRetainInstance (true)»; Например, в методе onCreate ().

Хорошо, в то время как метод Zsombor работает, это из-за того, что я неопытен с Фрагментами, и его решение вызывает проблемы с saveInstanceState Bundle .

По-видимому (по крайней мере, для DialogFragment), это должен быть public static class . Вы также должны написать свой собственный static DialogFragment newInstance() . Это связано с тем, что класс Fragment вызывает метод newInstance методе instantiate() .

Итак, в заключение, вы ДОЛЖНЫ писать свои диалоговые окна так:

 public static class MyDialogFragment extends DialogFragment { static MyDialogFragment newInstance() { MyDialogFragment d = new MyDialogFragment(); return d; } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { ... } } 

И покажите им:

 private void showMyDialog() { MyDialogFragment d = MyDialogFragment.newInstance(); d.show(getFragmentManager(), "dialog"); } 

Это может быть уникальным для библиотеки ActionBarSherlock, но официальные образцы в документации SDK также используют эту парадигму.

Чтобы преодолеть Bundle всегда являющийся нулевым, я сохраняю его в статическом поле onSaveInstanceState . Это запах кода, но единственное решение, которое я нашел для восстановления диалога и сохранения состояния.

Ссылка Bundle должна быть указана в onDestroy .

 @Override public void onCreate(Bundle savedInstanceState) { if (savedInstanceState == null) savedInstanceState = HackishSavedState.savedInstanceState; setRetainInstance(true); } @Override public Dialog onCreateDialog(Bundle savedInstanceState) { if (savedInstanceState == null) savedInstanceState = HackishSavedState.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) { ... HackishSavedState.savedInstanceState = outState; super.onSaveInstanceState(outState); } @Override public void onDestroy() { HackishSavedState.savedInstanceState = null; super.onDestroy(); } private static class HackishSavedState { static Bundle savedInstanceState; } 

Я использовал сочетание представленных решений и добавил еще одну вещь. Это мое окончательное решение:

Я использовал setRetainInstance (true) в onCreateDialog; Я использовал это:

 public void onDestroyView() { if (getDialog() != null && getRetainInstance()) getDialog().setDismissMessage(null); super.onDestroyView(); } 

И в качестве обходного пути saveInstanceState не работает, я создал закрытый класс StateHolder (так же, как владелец создает для listView):

 private class StateHolder { String name; String quantity; } 

Я сохраняю состояние таким образом:

 @Override public void onSaveInstanceState(Bundle savedInstanceState) { super.onSaveInstanceState(savedInstanceState); stateHolder = new StateHolder(); stateHolder.name = actvProductName.getText().toString(); stateHolder.quantity = etProductQuantity.getText().toString(); } 

В методе onDismiss я возвращаю stateHolder в значение null. Когда диалог создается, он проверяет, не является ли stateHolder нулевым, чтобы восстановить состояние или просто инициализировать все нормально.

Я решил эту проблему с ответами @ ZsomborErdődy-Nagy и @AndyDennie. Вы должны подклассифицировать этот класс, а в родительском фрагменте вызова setRetainInstance(true) и dialogFragment.show(getFragmentManager(), "Dialog");

  public class AbstractDialogFragment extends DialogFragment { @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); } @Override public void onDestroyView() { if (getDialog() != null && getRetainInstance()) getDialog().setDismissMessage(null); super.onDestroyView(); } } 

У меня была аналогичная проблема, но ни одно из вышеизложенных не работало для меня. В конце концов мне нужно было создать фрагмент в коде вместо XML-макета.

См .: Замена фрагментов и изменение ориентации

Я столкнулся с этим в своем проекте, и ни одно из вышеперечисленных решений не помогло.

Если исключение выглядит примерно так

 java.lang.RuntimeException: Unable to start activity ComponentInfo{ ... Caused by: java.lang.IllegalStateException: Fragment.... did not create a view. 

Это вызвано проблемой с резервным идентификатором контейнера, который используется после вращения. См. Этот билет для получения дополнительной информации:

https://code.google.com/p/android/issues/detail?id=18529

В принципе, вы можете предотвратить крах, убедившись, что все ваши фрагменты xml имеют тег, определенный в макете. Это предотвращает возникновение аварийного состояния, если вы вращаетесь, когда фрагмент виден.

В моем случае я смог применить это исправление без необходимости переопределять onDestroyView () или setRetainInstance (true), что является общей рекомендацией для этой ситуации.

Я столкнулся с этой проблемой, и onDestroyView() не работал. Оказалось, что это произошло потому, что я делал довольно интенсивное диалоговое создание в onCreate() . Это включало сохранение ссылки на AlertDialog , которую я бы затем вернул в onCreateDialog() .

Когда я переместил весь этот код в onCreateDialog() и прекратил сохранять ссылку на диалог, он снова начал работать. Я ожидаю, что я DialogFragment один из инвариантов, который DialogFragment имеет об управлении своим диалогом.

В onCreate() вызовите setRetainInstance(true) а затем setRetainInstance(true) это:

 @Override public void onDestroyView() { if (getDialog() != null && getRetainInstance()) { getDialog().setOnDismissMessage(null); } super.onDestroyView(); } 

Когда вы вызываете setRetainInstance(true) в onCreate (), onCreate () больше не будет вызываться через изменения ориентации, но onCreateView () все равно будет вызываться.

Таким образом, вы все равно можете сохранить состояние в своем пакете в onSaveInstanceState() а затем восстановить его в onCreateView() :

 @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("myInt", myInt); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = inflater.inflate(R.layout.my_layout, container); if (savedInstanceState != null) { myInt = savedInstanceState.getInt("myInt"); } ... return view; }