Взлом Android Fragment View State Loss при использовании FragmentTransaction.replace()

У меня довольно большая проблема, и я не совсем понимаю, что происходит. Я разрабатываю приложение, которое использует Fragments (из библиотеки поддержки), и использую FragmentTransaction.replace(), чтобы поместить новые фрагменты в задний стек и заменить старый. Код выглядит следующим образом:

FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = ft.beginTransaction();
// Animations in my res/anim folder
ft.setCustomAnimations(R.anim.slide_in_right, R.anim.slide_out_left, R.anim.slide_in_left, R.anim.slide_out_right);
ft.replace(R.id.fragment_container, newFragment, tag);
ft.addToBackStack(null);
ft.commit();

Это успешно заменяет мой фрагмент. Моя проблема заключается в следующем. В одном фрагменте у меня есть список элементов, которые создаются из пользовательского ввода. Теперь, когда пользователь нажимает кнопку "Далее", а затем нажимает кнопку "Назад" (чтобы вернуться к списку), список пуст, потому что представление уничтожено. Теперь я отметил следующее:

  • onSaveInstanceState не вызывается. Я считаю, что это происходит потому, что это вызывается только, когда говорит родительская активность. Основываясь на документах: "Существует много ситуаций, когда фрагмент может быть в основном снесен (например, когда он помещен в задний стек без отображения пользовательского интерфейса), но его состояние не будет сохранено до тех пор, пока его собственная деятельность не должна фактически сохранять свое состояние.". По-видимому, выполнение замены на FragmentTransaction не является одним из тех случаев. Кто-нибудь имеет подтверждение по этому или лучшему объяснению?
  • setOnRetainInstanceState (true) не помогает в этой ситуации. Опять же, я считаю, что это связано с информацией из документов: "Контролируйте, сохраняется ли экземпляр фрагмента в процессе повторного создания активности (например, при изменении конфигурации)". Я не выполняю никаких действий при воссоздании активности, поэтому это бесполезно.

Итак, я думаю, мой главный вопрос: есть ли способ сохранить состояние View (просто сохранить фрагмент) при использовании replace? Существует FragmentTransaction.add(), но есть несколько проблем с этим. Один из них заключается в том, что анимация выхода не выполняется, поэтому анимация неверна. Другим является то, что новый фрагмент, который старый фрагмент (тот, который помещается в невидимое состояние), остается кликабельным. Например, если у меня есть ListFragment, и я помещаю фрагмент содержимого поверх этого с помощью добавления, я все равно могу щелкнуть элементы списка в ListFragment.

Ответ 1

Не имея возможности увидеть код ваших фрагментов, это немного догадка, но в прошлом я столкнулся с этой же проблемой, и я обнаружил, что перезагрузка адаптера в вашем ListFragment в onViewStateRestored похоже, делает трюк.

public void onViewStateRestored (Bundle savedInstanceState)
{
    super.onViewStateRestored (savedInstanceState);
    setListAdapter(new ArrayAdapter(Activity, R.layout.nav_item, objects));
}

Что странно, учитывая, что в документации указано, что этот метод вызывается после onActivityCreated, но до onStart. Но похоже, что он также вызывается в другое время, потому что, когда самая последняя транзакция фрагмента выталкивается из заднего стека, этот метод вызывается до отображения ранее замещенного фрагмента. Активность, которая владеет фрагментами, никоим образом не была приостановлена ​​или не затенена, поэтому в соответствии с документами onViewStateRestored не следует вызывать, поскольку только фрагменты были изменены. Но это все равно работает.

Ответ 2

Похоже, вам просто нужно убедиться, что вы правильно реализовали onCreateView и onDestroyView. Ситуация, о которой вы описываете, кажется, указывает на то, что когда фрагмент списка помещается в задний стек (в результате транзакции замены), Android вызывает onDestroyView, чтобы освободить некоторые ресурсы. Однако, по-видимому, он не уничтожил фрагмент списка, потому что когда вы возвращаете назад, вы возвращаете один и тот же экземпляр фрагмента.

Предполагая, что это все правда, тогда, когда пользователь отбросит назад, Android вызовет onCreateView. Любое состояние, которое вы сохранили в переменных экземпляра фрагмента, все равно должно быть там, и все, что вам нужно сделать, это повторить отображение... возможно, установите адаптер в ListView или что-то еще.

Также убедитесь, что ваш обратный вызов onSaveInstanceState() фактически сохраняет состояние экземпляра, которое необходимо перестроить. Таким образом, если фрагмент на самом деле полностью уничтожен, FragmentManager может восстановить состояние, когда ему нужно повторно отделить фрагмент позже.