Экземпляр фрагмента сохраняется, но дочерний фрагмент не повторно прикреплен

Обновление: принятые ответы на вопросы объяснения (ошибка) с обходом, но также см. Мою работу на основе Kotlin, прилагаемую в качестве ответа ниже.

Этот код находится в Kotlin, но я думаю, что это основной цикл жизненного цикла Android.

У меня есть фрагмент, который содержит ссылку на другой «субфрагмент»,

Вот в основном то, что я делаю:

  1. У меня есть основной фрагмент, у которого retainInstance установлено значение true
  2. У меня есть поле в основном фрагменте, который будет содержать ссылку на подфрагмент, изначально это поле имеет значение null
  3. В onCreateView основного фрагмента я проверяю, является ли поле подфрагмента нулевым, если это так, я создаю экземпляр субфрагмента и назначаю его в поле
  4. Наконец, я добавляю подфрагмент в контейнер в макете основного фрагмента.
  5. Если поле не является нулевым, то есть мы находимся в onCreateView из-за изменения конфигурации, я не воссоздаю подфрагмент, я просто пытаюсь добавить его в контейнер.

Когда устройство повернуто, я наблюдаю onPaused() и onDestroyView() , но я не вижу, чтобы какие-либо методы lifecyle вызывались на подфрагменте во время добавления сохраненной ссылки на подфрагмент, к child_container, когда воссоздается представление основных фрагментов.

Сетевой аффект заключается в том, что я не вижу вид субфрагмента в основном фрагменте. Если я прокомментирую if (subfragment == null) и просто создаю новый подфрагмент каждый раз, я вижу подфрагмент в представлении.

Обновить

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

Я добавил код в действие по onWindowFocusChanged и я вижу что-то подобное при первом запуске приложения:

 activity is in view fm = FragmentManager{b13b9b18 in Tab1Fragment{b13b2b98}} tab 1 fragments = [DefaultSubfragment{b13bb610 #0 id=0x7f0c0078}] 

а затем после вращения:

 activity is in view fm = FragmentManager{b13f9c30 in Tab1Fragment{b13b2b98}} tab 1 fragments = null 

здесь fm является childFragmentManager, и, как вы можете видеть, у нас все еще есть тот же экземпляр Tab1Fragment, но у него есть новый childFragmentManager, который, как мне кажется, нежелателен, и из-за ошибки, о которой сообщается в ответе ниже. Дело в том, что я добавил субфрагмент к этому новому childFragmentManger. Таким образом, похоже, что транзакция никогда не выполняется со ссылкой на фрагмент, который был сохранен, но завершается, если я создаю новый фрагмент. (Я попытался вызвать executePendingTransactions для нового childFragmentManager)


 class Tab1Fragment: Fragment() { var subfragment: DefaultSubfragment? = null override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { val rootView = inflater!!.inflate(R.layout.fragment_main, container, false) if (subfragment == null ) { subfragment = DefaultSubfragment() subfragment!!.sectionLabel = "label 1" subfragment!!.buttonText = "button 1" } addRootContentToContainer(R.id.child_container, content = subfragment!!) return rootView } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) retainInstance = true } inline fun Fragment.addRootContentToContainer(containerId: Int, content: Fragment) { val transaction = childFragmentManager.beginTransaction() transaction.replace(containerId, content) transaction.commit() } 

Ваша проблема похожа на проблему, описанную здесь:

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

к сожалению, эта проблема, вероятно, не будет устранена Google.

Использование сохраненных фрагментов для пользовательского интерфейса или вложенных фрагментов не является хорошей идеей – они рекомендуются для использования вместо onRetainNonConfigurationInstance, т. Е. для больших коллекций / структур данных. Также вы можете найти Loaders лучше, чем сохраненные фрагменты, они также сохраняются во время изменений конфигурации.

Кстати. Я считаю, что android:configChanges фрагменты больше взлома – например, с помощью android:configChanges для «исправления» проблем, вызванных вращением экрана. Все работает, пока пользователь не нажимает на главный экран, и андроид решает убить ваш процесс приложения. Как только пользователь захочет вернуться к вашему приложению – ваши сохраненные фрагменты будут уничтожены – и вам все равно придется его воссоздать. Поэтому всегда лучше кодировать все, как если бы ваши ресурсы могли быть уничтожены в любое время.

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

Одна из должностей обеспечивает работу (которая, кажется, работает хорошо). Работа вокруг предполагает создание подкласса Fragment и использование отражения.

Поскольку мой оригинальный вопрос использовал код Котлина, я думал, что поделился бы своей версией Kotlin работы здесь, если кто-то еще ударит по этому поводу. В конце концов, я не уверен, что буду придерживаться этого решения, поскольку он по-прежнему является хакером, он все еще манипулирует частными полями, однако, если имя поля изменено, ошибка будет найдена во время компиляции, а не во время выполнения.

Как это работает:

  1. В вашем фрагменте, который будет содержать дочерние фрагменты, вы создадите поле keepedChildFragmentManager, которое будет удерживать childFragmentManager, который будет потерян во время изменения конфигурации
  2. В обратном вызове onCreate для того же фрагмента вы устанавливаете значение keepInstance равным true
  3. В обратном вызове onAttach для одного и того же фрагмента вы проверяете, не является ли keepedChildFragmentManger недействительным, если вы вызываете функцию расширения фрагмента, которая повторно присоединяет keepedChildFragmentManager, в противном случае вы установите keepedChildFragmentManager текущему childFragmentManager.
  4. Наконец, вам нужно исправить дочерние фрагменты, чтобы указать на вновь созданный хостинг (ошибка оставляет их ссылкой на старую активность, что, я думаю, приводит к утечке памяти).

Вот пример:

Расширения фрагментов Kotlin

 // some convenience functions inline fun Fragment.pushContentIntoContainer(containerId: Int, content: Fragment) { val transaction = fragmentManager.beginTransaction() transaction.replace(containerId, content) transaction.addToBackStack("tag") transaction.commit() } inline fun Fragment.addRootContentToContainer(containerId: Int, content: Fragment) { val transaction = childFragmentManager.beginTransaction() transaction.replace(containerId, content) transaction.commit() } // here we address the bug inline fun Fragment.reattachRetainedChildFragmentManager(childFragmentManager: FragmentManager) { setChildFragmentManager(childFragmentManager) updateChildFragmentsHost() } fun Fragment.setChildFragmentManager(childFragmentManager: FragmentManager) { if (childFragmentManager is FragmentManagerImpl) { mChildFragmentManager = childFragmentManager // mChildFragmentManager is private to Fragment, but the extension can touch it } } fun Fragment.updateChildFragmentsHost() { mChildFragmentManager.fragments.forEach { fragment -> // fragments is hidden in Fragment fragment?.mHost = mHost // mHost is private also } } 

Фрагмент Хостинг ребенка Фрагменты

 class Tab1Fragment : Fragment() , TabRootFragment { var subfragment: DefaultSubfragment? = null var retainedChildFragmentManager: FragmentManager? = null override val title = "Tab 1" override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { val rootView = inflater!!.inflate(R.layout.fragment_main, container, false) if (subfragment == null ) { subfragment = DefaultSubfragment() subfragment!!.sectionLable = "label 1x" subfragment!!.buttonText = "button 1" addRootContentToContainer(R.id.child_container, content = subfragment!!) } return rootView } override fun onAttach(context: Context?) { super.onAttach(context) if (retainedChildFragmentManager != null) { reattachRetainedChildFragmentManager(retainedChildFragmentManager!!) } else { retainedChildFragmentManager = childFragmentManager } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) retainInstance = true } } 
Intereting Posts
Ошибка " не может быть преобразована в JSON" при попытке создать объект JSON из String Moshi: ожидается класс, ParameterizedType или GenericArrayType, но <null> имеет тип null Ошибка в валидаторе привязки данных от Ilhasoft + Kotlin Kotlin – Как решить между «lateinit» и «nullable variable»? Android Studio Execution не удалось выполнить задачу compilefreeDebugKotlin Android Studio не удалось с Kotlin DoubleClick использовать Kotlin программно Может ли кто-нибудь сказать мне, как работает компилятор kotlin? Какова его архитектура? Spring Data Neo4j 5's EntityScan содержит сопутствующие объекты при использовании Kotlin Побитовая операция «и» в котлин Kotlin – возможно, неправильно сообщая о неиспользуемом коде Ошибка сборки: ошибка: выполнение не выполнено для задачи: app: compileLightningLiteDebugKotlin '. > Ошибка компиляции JaVers обнаруживает изменения в childs, если изменяется свойство simple в корневом объекте Производительность при использовании Pair в Kotlin для Android бросить, если оператор в Котлине