Фрагментные транзакции, Фрагментарные транзакции

Во время выполнения FragmentManager может добавлять, удалять, заменять фрагменты и выполнять другие действия с фрагментами в ответ на взаимодействие с пользователем. Каждый набор изменений фрагмента, который вы фиксируете, называется транзакцией , и вы можете указать, что делать внутри транзакции, используя API, предоставляемые классом FragmentTransaction . Вы можете сгруппировать несколько действий в одну транзакцию — например, транзакция может добавить или заменить несколько фрагментов. Эта группировка может быть полезна, когда на одном экране отображается несколько одноуровневых фрагментов, например, в разделенных представлениях.

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

Вы можете получить экземпляр FragmentTransaction из FragmentManager , вызвав beginTransaction() , как показано в следующем примере:

Котлин

val fragmentManager = ...
val fragmentTransaction = fragmentManager.beginTransaction()

Ява

FragmentManager fragmentManager = ...
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

Последний вызов каждого FragmentTransaction должен подтвердить транзакцию. Вызов commit() сигнализирует FragmentManager о том, что все операции были добавлены в транзакцию.

Котлин

val fragmentManager = ...
// The fragment-ktx module provides a commit block that automatically
// calls beginTransaction and commit for you.
fragmentManager.commit {
    // Add operations here
}

Ява

FragmentManager fragmentManager = ...
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

// Add operations here

fragmentTransaction.commit();

Разрешить изменение порядка изменений состояния фрагмента

Каждая FragmentTransaction должна использовать setReorderingAllowed(true) :

Котлин

supportFragmentManager.commit {
    ...
    setReorderingAllowed(true)
}

Ява

FragmentManager fragmentManager = ...
fragmentManager.beginTransaction()
    ...
    .setReorderingAllowed(true)
    .commit();

Для совместимости поведения флаг переупорядочения по умолчанию не включен. Однако это необходимо, чтобы позволить FragmentManager правильно выполнить ваш FragmentTransaction , особенно когда он работает с обратным стеком и запускает анимацию и переходы. Включение флага гарантирует, что если несколько транзакций выполняются одновременно, любые промежуточные фрагменты (т. е. те, которые добавляются, а затем немедленно заменяются) не претерпевают изменений жизненного цикла или не выполняют свою анимацию или переходы. Обратите внимание, что этот флаг влияет как на первоначальное выполнение транзакции, так и на ее отмену с помощью popBackStack() .

Добавление и удаление фрагментов

Чтобы добавить фрагмент в FragmentManager , вызовите add() для транзакции. Этот метод получает идентификатор контейнера для фрагмента, а также имя класса фрагмента, который вы хотите добавить. Добавленный фрагмент переводится в состояние RESUMED . Настоятельно рекомендуется, чтобы контейнер представлял собой FragmentContainerView , который является частью иерархии представлений.

Чтобы удалить фрагмент с хоста, вызовите метод remove() , передав экземпляр фрагмента, полученный из менеджера фрагментов, с помощью findFragmentById() или findFragmentByTag() . Если представление фрагмента ранее было добавлено в контейнер, на этом этапе представление удаляется из контейнера. Удаленный фрагмент переводится в состояние DESTROYED .

Используйте replace() чтобы заменить существующий фрагмент в контейнере экземпляром нового класса фрагмента, который вы предоставляете. Вызов replace() эквивалентен вызову remove() с фрагментом в контейнере и добавлению нового фрагмента в тот же контейнер.

Следующий фрагмент кода показывает, как можно заменить один фрагмент другим:

Котлин

// Create new fragment
val fragmentManager = // ...

// Create and commit a new transaction
fragmentManager.commit {
    setReorderingAllowed(true)
    // Replace whatever is in the fragment_container view with this fragment
    replace<ExampleFragment>(R.id.fragment_container)
}

Ява

// Create new fragment and transaction
FragmentManager fragmentManager = ...
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setReorderingAllowed(true);

// Replace whatever is in the fragment_container view with this fragment
transaction.replace(R.id.fragment_container, ExampleFragment.class, null);

// Commit the transaction
transaction.commit();

В этом примере новый экземпляр ExampleFragment заменяет фрагмент, если таковой имеется, который в данный момент находится в контейнере макета, определенном R.id.fragment_container .

По умолчанию изменения, внесенные в FragmentTransaction , не добавляются в задний стек. Чтобы сохранить эти изменения, вы можете вызвать addToBackStack() в FragmentTransaction . Дополнительные сведения см. в разделе Менеджер фрагментов .

Фиксация является асинхронной

Вызов commit() не выполняет транзакцию немедленно. Скорее, транзакция будет запущена в основном потоке пользовательского интерфейса, как только она сможет это сделать. Однако при необходимости вы можете вызвать commitNow() , чтобы немедленно запустить транзакцию фрагмента в потоке пользовательского интерфейса.

Обратите внимание, что commitNow несовместим с addToBackStack . В качестве альтернативы вы можете выполнить все ожидающие FragmentTransactions отправленные вызовами commit() , которые еще не выполнялись, вызвав executePendingTransactions() . Этот подход совместим с addToBackStack .

Для подавляющего большинства случаев использования commit() — это все, что вам нужно.

Порядок операций важен

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

Котлин

supportFragmentManager.commit {
    setCustomAnimations(enter1, exit1, popEnter1, popExit1)
    add<ExampleFragment>(R.id.container) // gets the first animations
    setCustomAnimations(enter2, exit2, popEnter2, popExit2)
    add<ExampleFragment>(R.id.container) // gets the second animations
}

Ява

getSupportFragmentManager().beginTransaction()
        .setCustomAnimations(enter1, exit1, popEnter1, popExit1)
        .add(R.id.container, ExampleFragment.class, null) // gets the first animations
        .setCustomAnimations(enter2, exit2, popEnter2, popExit2)
        .add(R.id.container, ExampleFragment.class, null) // gets the second animations
        .commit()

Ограничить жизненный цикл фрагмента

FragmentTransactions может влиять на состояние жизненного цикла отдельных фрагментов, добавленных в область транзакции. При создании FragmentTransaction setMaxLifecycle() устанавливает максимальное состояние для данного фрагмента. Например, ViewPager2 использует setMaxLifecycle() чтобы ограничить фрагменты за кадром состоянием STARTED .

Отображение и скрытие видов фрагмента

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

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

Прикрепление и отсоединение фрагментов

Метод FragmentTransaction detach() отделяет фрагмент от пользовательского интерфейса, разрушая его иерархию представлений. Фрагмент остается в том же состоянии ( STOPPED ), как и при помещении его в задний стек. Это означает, что фрагмент был удален из пользовательского интерфейса, но по-прежнему управляется менеджером фрагментов.

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

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