Управление жизненным циклом Android Фрагментов в ViewPager и FragmentPagerAdapter

Я пытался выяснить, что такое правильное управление Фрагментами в FragmentActivity с ViewPager. Прежде чем вдаваться в подробности, краткое изложение проблемы, с которой я столкнулся, следующее:

У меня есть FragmentActivity с ViewPager. ViewPager использует пользовательский, но очень простой FragmentPagerAdapter. Каждый Fragment внутри ViewPager содержит ExpandableListView. У меня также есть кнопка панели действий под названием "Обновить". Пусть теперь ViewPager имеет только один Fragment. Действие создается, а Fragment ExpandableListView заполняется (пока что так хорошо). Когда нажата кнопка "Обновить", метод обработки в FragmentActivity выполняет итерацию по списку Fragments, которые назначаются FragmentPagerAdapter, и вызывает refresh() на каждом Fragment, чтобы заполнить свой ListView. Однако, когда ориентация устройства изменяется (например, от портрета к пейзажу), активность воссоздается, а также фрагменты. Нажатие кнопки "Обновить" теперь будет перебирать не инициализированный Fragments.

Я знаю, что я довольно расплывчатый, особенно без образца кода, но, пожалуйста, несите меня. Я проследил проблему и вызовы метода следующим образом с начала приложения/действия:

  • FragmentActivity.onCreate()
  • FragmentActivity.setContentView()
  • FragmentActivity.createPagerFragments() < - это создает ArrayList of Fragments и присваивает их новому FragmentPagerAdapter, который, в свою очередь, назначен ViewPager.
  • Fragment.onAttach()
  • Fragment.onCreate() < - ничего особенного здесь, просто вызов метода super.
  • Fragment.onCreateView() < - ничего особенного здесь, просто раздувание макета
  • Fragment.onActivityCreated() < - ничего здесь тоже.
  • < < Все хорошее, ориентация меняется здесь →
  • FragmentActivity.onCreate()
  • Fragment.onAttach()
  • Fragment.onCreate()
  • FragmentActivity.setContentView()
  • FragmentActivity.createPagerFragments()
  • Fragment.onCreateView()
  • Fragment.onActivityCreated()
  • < < Кнопка "Обновить" нажата →
  • FragmentActivity.refresh() < - итерации по вновь созданным фрагментам из # 13 (не для Android!).
  • < < Crash: NullPointerException для mExpandableListView в фрагменте. →

Таким образом, проблема, как я вижу, заключается в следующем: Когда Android повторно создает FragmentActivity и его Views после изменения ориентации экрана (вызовы # 9-15 выше), он создает новые объекты Fragment, состояние которых восстанавливается до исходных. Однако эти, по-видимому, полностью управляются FragmentManager, а не FragmentPagerAdapter. В отличие от этого, когда FragmentPagerAdapter воссоздается вместе с Fragments в методе активности onCreate (см. Вызов № 13), Fragments, которые назначаются адаптеру, никогда не имеют своих Fragment.onCreate() или Fragment.onCreateView() методы, называемые вообще. Поэтому, когда вызывается метод refresh() (см. # 17), метод выполняет итерацию по этим Fragments, которые не были инициализированы. Поэтому, когда они пытаются заполнить ExpandableListView, переменная экземпляра представления NULL. Этого следует ожидать, поскольку переменная экземпляра назначается только в методе Fragment.onCreateView(), который никогда не вызывается в этих Fragments.

Итак, мой вопрос: как правильно сделать повторное использование повторно воссозданного (Android) Fragments после изменения ориентации экрана, чтобы избежать создания новых, которые не Не инициализироваться? Мне нужно иметь действительную ссылку на них, чтобы вызвать их метод refresh(), который заполняет их по требованию. В идеале они также должны быть привязаны к FragmentPagerAdapter.

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

Спасибо!

Ответ 1

Это много читать, но после прочтения только введения и вопроса и опыта с FragmentStatePagerAdapter, который похож на FragmentPagerAdapter, я могу сказать вам, что:

После поворота ваш адаптер автоматически добавит старые фрагменты. Таким образом, кажется, что, хотя адаптер создания активности воссоздается, FragmentManager, который является глобальным, и этот экземпляр сохраняет активность, будет обнаруживать, что новый FragmentStatePagerAdapter объединен с одним и тем же ViewPager и запрашивает те же фрагменты и будет просто извлекать их из Fragment BackStack.

Вы, как дизайнер Фрагментов, можете заметить это поведение, продолжая призывать Fragment.onAttach() и Fragment.onDetach(). Когда onAttach() встречается, это либо создание вашего фрагмента, либо повторное использование его после вращения. Вы должны уметь отличать, что Фрагмент был повернут с использованием обратного вызова onRestoreRnstanceState().

В ваших журналах вы увидите много событий onCreate() и других состояний одновременно, потому что FragmentStatePagerAdapter всегда извлекает/создает min 3 фрагментов (кроме тех случаев, когда вы устанавливаете, что они всего 2 или 1), так что после поворота экрана 3 фрагмента будут быть повторно привязанным от задней части стола.

Я надеюсь, что это помогло.

Ответ 2

Я считаю, что этот question, о том, как извлечь текущий фрагмент из ViewPager, поможет вам. Как уже указывалось, фрагменты управляются с помощью Fragment (State) PagerAdapter и NOT Activity или Fragment lifecycle.

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

Ответ 3

Как добавить это в тег активности в манифест:

android:configChanges="orientation"

или для API 13 или выше

android:configChanges="orientation|screenSize" 

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