Фрагменты, Диалоговое Фрагмент и Вращение экрана
У меня есть 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.
Каким образом можно отобразить диалоговое окно Dialog из другого фрагмента, отличного от DialogFragment.show(FragmentManager, String)?
Ответы
Ответ 1
ОК, в то время как метод 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 также используют эту парадигму.
Ответ 2
В библиотеке совместимости есть ошибка, которая может вызвать это. Попробуйте поставить это в диалоговом окне:
@Override
public void onDestroyView() {
if (getDialog() != null && getRetainInstance())
getDialog().setOnDismissListener(null);
super.onDestroyView();
}
Я также предлагаю установить ваш диалог, как сохраненный, поэтому он не будет уволен после поворота. Положите "setRetainInstance (true)"; например в методе onCreate().
Ответ 3
Чтобы превзойти 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;
}
Ответ 4
Я использовал сочетание представленных решений и добавил еще одну вещь.
Это мое окончательное решение:
Я использовал 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 нулевым, чтобы восстановить состояние или просто инициализировать все нормально.
Ответ 5
Я решил эту проблему с ответами @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();
}
}
Ответ 6
У меня была аналогичная проблема, но ни одно из выше сказанного не работало для меня. В итоге мне нужно было создать фрагмент в коде вместо XML-макета.
Смотрите: Замена фрагментов и изменение ориентации
Ответ 7
Я столкнулся с этим в своем проекте, и ни одно из вышеперечисленных решений не помогло.
Если исключение выглядит примерно как
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), что является общей рекомендацией для этой ситуации.
Ответ 8
Я столкнулся с этой проблемой, и трюк onDestroyView()
не работал. Оказалось, что это произошло потому, что я делал довольно интенсивное диалоговое создание в onCreate()
. Это включало сохранение ссылки на AlertDialog
, которую я затем вернул бы в onCreateDialog()
.
Когда я переместил весь этот код в onCreateDialog()
и остановил сохранение ссылки на диалог, он снова начал работать. Я ожидаю, что я нарушу один из инвариантов, который DialogFragment
имеет об управлении своим диалогом.
Ответ 9
В onCreate()
вызовите 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;
}