Фрагмент без представления вылетает при изменении конфигурации

У меня есть пользовательский интерфейс, в котором мне нужно, чтобы Fragment отображался (с видом) в ландшафтном режиме, но не в портретном режиме. В портретном режиме он по-прежнему должен быть доступен, но вместо этого будет отображаться результат с использованием ListPopupWindow.

Я подумал, что смогу справиться с этим, используя тег <fragment /> для альбомной раскладки при программном создании фрагмента, если он не был запущен (в случае, когда мы находимся в портретной ориентации).

Это отлично работает, пока вы начинаете в альбомной ориентации, если вы начинаете в портретной ориентации, где фрагмент создается программно, ваше приложение падает при повороте эмулятора, когда он пытается прикрепить фрагмент к вашему макету:

java.lang.IllegalStateException: Фрагмент не создал представление. в android.app.Activity.onCreateView(Activity.java:4095)

Документы для Fragment.isInLayout(), похоже, намекают, что он должен обрабатывать это следующим образом:

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

Так вот вопрос, как это сделать правильно или я что-то упускаю?

ОБНОВЛЕНИЕ:

Похоже, что isInLayout() в настоящее время ведет себя не так, как должен. Он возвращает false, если вы добавили Fragment в контейнер вручную.

Кроме того, если вы добавите Fragment вручную в контейнер, а затем повернете (устройство) на макет, который не содержит этот макет, произойдет сбой:

Причина: java.lang.IllegalArgumentException: не найдено представление для идентификатора 0x7f060011 для фрагмента SearchFragment{4042f868 #2 id=0x7f060011 SearchFragment} в android.app.FragmentManagerImpl.moveToState(FragmentManager.java:722)


person alexanderblom    schedule 08.02.2011    source источник
comment
Я думаю, что с фрагментами и изменениями конфигурации происходят некоторые странности. Я отправил отчет об ошибке, который обнаружил примерно так: code .google.com/p/android/issues/detail?id=14796. Порядок обратных вызовов фрагмента не так задокументирован, когда приложение проходит ротацию устройства.   -  person Dave MacLean    schedule 18.02.2011
comment
У меня была эта проблема довольно давно. Я не знаю, применимо ли это к вам, но в моем случае добавление if (container == null) return null; в начале метода фрагмента onCreateView() предотвращает создание представления, что предотвращает загрузку Fragment, если она не нужна при изменении ориентации.   -  person Alex Curran    schedule 25.08.2011
comment
@espi, я пробовал это, но мой фрагмент все еще дает сбой, трассировка стека даже не попадает ни в один из моих кодов. У меня есть подозрение, что это как-то связано с моей настройкой setRetainInstance. Вы это устанавливаете?   -  person nmr    schedule 22.09.2011
comment
нет, у меня было много проблем с сохранением экземпляров, и мне не нужно было их сохранять, поэтому я просто избавился от экземпляров.   -  person Alex Curran    schedule 22.09.2011
comment
Я прочитал документацию для setRetainInstance, и там написано, что он не совместим с бэкстеком. Я использовал задний стек, так что это была моя проблема. developer.android.com/reference/android/app/ @hackbod: было бы здорово, если бы вы добавили одно из этих исключений для защиты от идиотов, если бы вы добавили фрагмент в стек с помощью setRetainInstance   -  person nmr    schedule 26.09.2011


Ответы (2)


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

Создайте два разных макета: один в каталоге layout, другой в каталоге layout-land. Тот, что находится в каталоге layout-land, будет использоваться в ландшафтном режиме. В качестве заполнителя, куда вы хотите, чтобы ваш фрагмент попал, используйте элемент FrameLayout и присвойте ему идентификатор, скажем, с идентификатором «my_fragment». Макет в каталоге макетов не должен содержать элементов с таким идентификатором.

В методе onCreate используйте findViewById(R.id.my_fragment), чтобы найти местозаполнитель фрагмента. Если он существует, вы находитесь в ландшафтном режиме и должны добавить свой фрагмент (если он еще не существует): add(R.id.my_fragment, new MyFragment, «myFragment»). не создавать фрагмент.

Будьте очень осторожны, никогда не заменяйте фрагмент, созданный с помощью тега, фрагментом, созданным динамически в вашей программе. Фрагмент, для которого isInLayout возвращает true, — это совсем другой зверь, чем тот, для которого он возвращает false. Их жизненные циклы совершенно разные. Замена одного другим приведет к ужасной проблеме IllegalStateException «Фрагмент не создал представление».

-блейк

person Blake Meike    schedule 06.04.2011
comment
Спасибо за разбор проблемы. Полдня бился над этой проблемой, прежде чем нашел ваше объяснение. - person mach; 03.01.2012

Ваша проблема также может быть связана с тем, что у фрагмента, который вы используете, нет горизонтального макета. У вас может быть один для портрета, и поэтому ваша программа работает нормально, но когда вы поворачиваете свое устройство, ОС, вероятно, ищет вид в альбомной папке и не находит вид, поэтому объявляет его отсутствующим. Убедитесь, что у вас есть просмотр как в папке "layout", так и в "layout-land".

person Marco RS    schedule 04.01.2012
comment
Если в макете нет представления, Android просто использует его в макете, так что это не проблема. - person jdempcy; 22.10.2020