片段交易

在執行階段,FragmentManager 可根據使用者互動內容來新增、移除、取代及執行其他包含片段的動作。您修訂的每組片段變更都稱為「交易」,且您可以使用 FragmentTransaction 類別提供的 API 指定交易過程中要執行的作業。您可以將多個動作分入單一交易。舉例來說,某個交易可新增或取代多個片段。如要在同一畫面上顯示多個同層片段 (例如分割檢視),這種分組做法就會很實用。

您可以將每個交易儲存至由 FragmentManager 管理的返回堆疊,讓使用者透過片段變更返回瀏覽 (原理類似瀏覽先前的活動)。

您可以呼叫 beginTransaction() 來取得 FragmentManager 的 FragmentTransaction 例項,如以下範例所示:

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()。這個方法會接收片段「容器」的 ID,以及待新增片段的類別名稱。新增的片段會移至 RESUMED 狀態。我們強烈建議您將「容器」視為屬於檢視區塊階層一部分的 FragmentContainerView。

如要從主機中移除片段,請呼叫 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 中所做的變更不會新增至返回堆疊。如要儲存這些變更,您可以針對 FragmentTransaction 呼叫 addToBackStack()。詳情請參閱「片段管理員」一文。

修訂作業為非同步性質

呼叫 commit() 後,系統不會立即執行交易,而會安排盡快在主要 UI 執行緒中執行交易。不過,您可以視需要呼叫 commitNow(),立即在 UI 執行緒中執行片段交易。

請注意,commitNow 與 addToBackStack 不相容。或者,您也可以呼叫 executePendingTransactions(),藉此執行已由 commit() 提交但尚未執行的所有待處理 FragmentTransactions。這個做法與 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() 會將片段從 UI 卸離,並刪除其檢視區塊階層。片段會繼續處於其在返回堆疊中的狀態 (STOPPED)。這表示片段雖然已從 UI 中移除,但仍是由片段管理員所管理。

attach() 方法會重新附加先前已卸離的片段。這會導致系統重新建立檢視區塊階層、將階層附加至 UI 並顯示該階層。

系統會將 FragmentTransaction 視為一組不可拆分的作業,因此如果在同一交易中針對相同片段例項同時呼叫 detach 和 attach,兩者實際上會互相抵銷,避免系統刪除並立即重新建立片段的 UI。如要卸離並立即重新附加某個片段,請使用不同的交易並以 executePendingOperations() 分隔 (如有使用 commit())。