Транзакции фрагментов

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

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

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

Kotlin

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

Java

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

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

Kotlin

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

Java

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

// Add operations here

fragmentTransaction.commit();

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

Каждый объект FragmentTransaction должен использовать setReorderingAllowed(true):

Kotlin

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

Java

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

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

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

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

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

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

В следующем фрагменте кода показано, как заменить один фрагмент другим:

Kotlin

// 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)
}

Java

// 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() не приводит к немедленному выполнению транзакции. Вместо этого транзакция планируется к выполнению в основном потоке UI, как только это станет возможным. Однако при необходимости вы можете вызвать метод commitNow(), чтобы немедленно запустить транзакцию фрагмента в потоке UI.

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

В большинстве случаев достаточно использовать commit().

Порядок операций имеет значение

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

Kotlin

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
}

Java

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() отсоединяет фрагмент от пользовательского интерфейса, уничтожая его иерархию View. Фрагмент остается в том же состоянии (STOPPED), в котором он был помещен в обратный стек. Это означает, что фрагмент был удален из пользовательского интерфейса, но по-прежнему управляется менеджером фрагментов.

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

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