FragmentManager
是負責對應用程式片段執行操作的類別,例如新增、移除或取代這些片段,並將其新增至返回堆疊。
如果您使用的是 Jetpack Navigation 程式庫,可能永遠不會與 FragmentManager
直接互動,因為該程式庫會代表您使用 FragmentManager
。不過,任何使用片段的應用程式都會在某些程度上使用 FragmentManager
,因此請務必瞭解這項類別與其運作方式。
本頁面將說明:
- 如何存取
FragmentManager
。 - 與活動和片段相關的
FragmentManager
角色。 - 如何使用
FragmentManager
管理返回堆疊。 - 如何為片段提供資料和依附元件。
存取 FragmentManager
您可以透過活動或片段存取 FragmentManager
。
FragmentActivity
及其子類別 (例如 AppCompatActivity
) 都能透過 getSupportFragmentManager()
方法存取 FragmentManager
。
片段可代管一或多個子項片段。在片段中,您可以透過 getChildFragmentManager()
取得用來管理片段子項的 FragmentManager
參照。如果您需要存取其主機 FragmentManager
,可以使用 getParentFragmentManager()
。
以下列舉幾個範例,用來說明片段、片段的主機,以及與每個片段相關聯的 FragmentManager
例項彼此的關係。

圖 1 顯示兩個範例,每個範例都只有單一活動主機。這兩個示例中的主機活動都會以 BottomNavigationView
的形式向使用者顯示頂層導覽,該元素負責在應用程式中使用不同的螢幕畫面換出主機片段,每個螢幕畫面都會實作為獨立的片段。
範例 1 中的主機片段代管兩個子項片段,兩個片段組成分割檢視螢幕畫面。範例 2 中的主機片段代管一個子項片段,該片段組成滑動檢視畫面的顯示片段。
透過這項設定,您可以將每個主機視為皆具備與其相關聯的 FragmentManager
,用於管理主機的子項片段。圖 2 顯示了相關說明,以及 supportFragmentManager
、parentFragmentManager
和 childFragmentManager
之間的屬性對應。

FragmentManager
,且用於管理子項片段。應參照的 FragmentManager
屬性取決於呼叫網站在片段階層中的位置,以及您嘗試存取的片段管理員。
取得 FragmentManager
的參照後,即可用其操作向使用者顯示的片段。
子項片段
一般而言,應用程式是由應用程式專案中的單一或少量活動組成,每個活動都代表一組相關的螢幕畫面。該活動可能會提供一個放置頂層導覽的位置,並提供另一個位置來限制 ViewModel
和片段之間其他檢視畫面狀態的範圍。片段代表應用程式中的個別目的地。
如果您想一次顯示多個片段 (例如在分割檢視畫面或資訊主頁中顯示),可以使用由目的地片段及其子項片段管理員管理的子項片段。
子項片段的其他用途如下:
- 螢幕滑動:使用父項片段中的
ViewPager2
,用於管理一系列子項片段檢視畫面。 - 在一組相關的螢幕畫面中進行子導覽。
- Jetpack 導航使用子項片段做為個別到達網頁。一個活動代管一個父項
NavHostFragment
,並在使用者瀏覽應用程式時,將不同的子項目的地片段填入其空間。
使用 FragmentManager
FragmentManager
會管理片段返回堆疊。FragmentManager
可以在執行階段執行返回堆疊作業,例如新增或移除片段,以回應使用者互動。每一組異動都會以一個單位 (稱為 FragmentTransaction
) 的形式提交。如要深入瞭解片段交易,請參閱片段交易指南。
當使用者輕觸裝置上的返回按鈕,或呼叫 FragmentManager.popBackStack()
時,最頂端的片段交易會從堆疊中彈出。如果堆疊上沒有其他片段交易,且您未使用子項片段,則返回事件會向上傳遞至活動。如果您「有」使用子項片段,請參閱「子項片段和同層級片段的特別注意事項」。
當您在交易中呼叫 addToBackStack()
時,交易可以包含任意數量的作業,例如新增多個片段,或取代多個容器中的片段
彈出返回堆疊時,所有作業都會撤銷,成為不可中斷的單項作業。不過,如果您在呼叫 popBackStack()
前提交了其他交易,且您「並未」對交易使用 addToBackStack()
,則這些操作「不會」撤銷。因此,在單一 FragmentTransaction
中,請避免將會影響返回堆疊的交易,以及不會影響返回堆疊的交易交錯處理。
執行交易
如要在版面配置容器中顯示片段,請使用 FragmentManager
來建立 FragmentTransaction
。然後,您可以在交易中對容器執行 add()
或 replace()
作業。
舉例來說,簡易的 FragmentTransaction
可能如下所示:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("name") // Name can be null }
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack("name") // Name can be null .commit();
在本例中,ExampleFragment
會取代目前在版面配置容器中的片段 (如有),該容器是由 R.id.fragment_container
ID 識別。將片段的類別提供給 replace()
方法,可讓 FragmentManager
使用其 FragmentFactory
處理例項化。詳情請參閱「為片段提供依附元件」一節。
setReorderingAllowed(true)
會將交易內的片段狀態異動最佳化,因此動畫和轉場可以正常運作。如要進一步瞭解如何使用動畫和轉場效果,請參閱片段交易和使用動畫瀏覽各個片段。
呼叫 addToBackStack()
會將交易提交至返回堆疊。使用者稍後可以輕觸「返回」按鈕來撤銷交易,恢復前一個片段。如果您在單一交易中新增或移除了多個片段,彈出返回堆疊時,所有這些操作都會撤銷。addToBackStack()
呼叫中提供的選用名稱可讓您使用 popBackStack()
,彈回至該特定交易。
如果您在執行移除片段的交易時呼叫 addToBackStack()
,則在提交交易時,系統會刪除已移除的片段,且使用者無法回到該片段。如果您在移除某個片段時呼叫 addToBackStack()
,則該片段只會 STOPPED
,而之後使用者返回時,該片段會 RESUMED
。在這種情況下,該檢視畫面會遭到刪除。詳情請參閱「片段生命週期」一文。
找出現有片段
您可以使用 findFragmentById()
取得版面配置容器中當前片段的參照。使用 XML 時,請使用 findFragmentById()
查詢指定 ID 的片段;如要新增標記,請在 FragmentTransaction
中加入容器 ID。範例如下:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as ExampleFragment
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentById(R.id.fragment_container);
或者,您也可以使用 findFragmentByTag()
,為片段指派專屬標記並取得參照。您可以在版面配置中定義的片段上使用 android:tag
XML 屬性指派標記,或在 FragmentTransaction
中的 add()
或 replace()
操作期間進行這項作業。
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container, "tag") setReorderingAllowed(true) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentByTag("tag") as ExampleFragment
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null, "tag") .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");
子項片段和同層級片段的特別注意事項
在任何特定時間內,只有一個 FragmentManager
可以控制片段返回堆疊。如果應用程式同時在螢幕畫面上顯示多個同層級片段,或使用子項片段,則須指定一個 FragmentManager
來處理應用程式的主要導覽。
如要定義片段交易中的主要導覽片段,請在交易中呼叫 setPrimaryNavigationFragment()
方法,並傳入一個片段的例項,該例項的 childFragmentManager
應具有主要控制權。
請將導覽結構視為一系列層級,將活動放在最外層,納入底下的每一層子項片段。每一層都有一個主要導覽片段。
發生返回事件時,最內層會控制導覽行為。一旦最內層沒有任何片段交易可彈回,控制權會回到外面一層,並重複此過程,直到到達活動為止。
同時顯示兩個以上的片段時,只有一個可以是主要導覽片段。如果將某個片段設為主要導覽片段,系統會移除對先前片段的標示。在上述示例中,如果將詳細資料片段設為主要導覽片段,系統會移除對主要片段的標示。
支援多個返回堆疊
在某些情況下,您的應用程式可能需要支援多個返回堆疊。最常見的例子是應用程式使用底部導覽列。FragmentManager
可讓您使用 saveBackStack()
和 restoreBackStack()
方法支援多個返回堆疊。這些方法可讓您儲存一個返回堆疊並還原不同的堆疊,藉此在返回堆疊間切換。
saveBackStack()
的運作方式與呼叫 popBackStack()
相同,其中包含選用的 name
參數:彈出指定交易和堆疊上之後的所有交易。差別在於 saveBackStack()
會儲存彈出交易中所有片段的狀態。
舉例來說,假設您先前藉由使用 addToBackStack()
提交 FragmentTransaction
,將片段新增至返回堆疊,如以下範例所示:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("replacement") }
Java
supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) // setReorderingAllowed(true) and the optional string argument for // addToBackStack() are both required if you want to use saveBackStack() .setReorderingAllowed(true) .addToBackStack("replacement") .commit();
在這種情況下,您可以呼叫 saveBackStack()
來儲存這個片段交易和 ExampleFragment
的狀態:
Kotlin
supportFragmentManager.saveBackStack("replacement")
Java
supportFragmentManager.saveBackStack("replacement");
您可以使用相同的名稱參數呼叫 restoreBackStack()
,以還原所有彈出的交易與所有已儲存的片段狀態:
Kotlin
supportFragmentManager.restoreBackStack("replacement")
Java
supportFragmentManager.restoreBackStack("replacement");
為片段提供依附元件
新增片段時,您可以手動將片段執行個體化,並將其新增至 FragmentTransaction
。
Kotlin
fragmentManager.commit { // Instantiate a new instance before adding val myFragment = ExampleFragment() add(R.id.fragment_view_container, myFragment) setReorderingAllowed(true) }
Java
// Instantiate a new instance before adding ExampleFragment myFragment = new ExampleFragment(); fragmentManager.beginTransaction() .add(R.id.fragment_view_container, myFragment) .setReorderingAllowed(true) .commit();
提交片段交易時,系統便會使用您建立的片段執行個體。但是,在設定變更期間,系統會將您的活動和所有片段都刪除,並使用最適用的 Android 資源重新建立。FragmentManager
會為您處理以下所有操作:重新建立片段的例項、將其連接至主機,並重新建立返回堆疊狀態。
根據預設,FragmentManager
會使用架構提供的 FragmentFactory
,將片段的新例項進行例項化。這個預設工廠會利用反射來查找並叫用片段的無引數建構函式。也就是說,您無法使用這個預設工廠為片段提供依附元件。這表示在預設情況下,重新建立的過程中「不會」使用您首次建立片段時所用的任何自訂建構函式。
如要為片段提供依附元件,或是使用任何自訂建構函式,請改為建立自訂 FragmentFactory
子類別,然後覆寫 FragmentFactory.instantiate
。接著,您可以使用自訂工廠覆寫 FragmentManager
的預設工廠,然後用它為片段進行例項化。
假設您有負責顯示您家鄉熱門甜點的 DessertsFragment
,且 DessertsFragment
在 DessertsRepository
類別上有依附元件,而該類別會為其提供向使用者顯示正確 UI 所需的資訊。
您可以將 DessertsFragment
定義為在其建構函式中需要 DessertsRepository
例項。
Kotlin
class DessertsFragment(val dessertsRepository: DessertsRepository) : Fragment() { ... }
Java
public class DessertsFragment extends Fragment { private DessertsRepository dessertsRepository; public DessertsFragment(DessertsRepository dessertsRepository) { super(); this.dessertsRepository = dessertsRepository; } // Getter omitted. ... }
實作簡單的 FragmentFactory
可能如下所示。
Kotlin
class MyFragmentFactory(val repository: DessertsRepository) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment = when (loadFragmentClass(classLoader, className)) { DessertsFragment::class.java -> DessertsFragment(repository) else -> super.instantiate(classLoader, className) } }
Java
public class MyFragmentFactory extends FragmentFactory { private DessertsRepository repository; public MyFragmentFactory(DessertsRepository repository) { super(); this.repository = repository; } @NonNull @Override public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) { Class<? extends Fragment> fragmentClass = loadFragmentClass(classLoader, className); if (fragmentClass == DessertsFragment.class) { return new DessertsFragment(repository); } else { return super.instantiate(classLoader, className); } } }
這個範例子類別 FragmentFactory
覆寫 instantiate()
方法,以便提供 DessertsFragment
的自訂片段建立邏輯。其他片段類別是透過 super.instantiate()
由 FragmentFactory
的預設行為進行處理。
然後,您可以在 FragmentManager
上設定屬性,將 MyFragmentFactory
指定為用來建構應用程式片段的工廠。您必須在活動的 super.onCreate()
之前設定這個屬性,以確保在重新建立片段時會使用 MyFragmentFactory
。
Kotlin
class MealActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = MyFragmentFactory(DessertsRepository.getInstance()) super.onCreate(savedInstanceState) } }
Java
public class MealActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { DessertsRepository repository = DessertsRepository.getInstance(); getSupportFragmentManager().setFragmentFactory(new MyFragmentFactory(repository)); super.onCreate(savedInstanceState); } }
在活動中設定 FragmentFactory
,會覆寫整個活動的片段階層中的片段建立。換句話說,您新增的任何子項片段 childFragmentManager
都會使用在此設定的自訂片段工廠,除非您在較低層級覆寫該片段。
使用 FragmentFactory 進行測試
請在單一活動架構中使用 FragmentScenario
類別單獨測試片段。由於您無法依賴活動的自訂 onCreate
邏輯,請改將 FragmentFactory
做為引數傳送至片段測試,如以下範例所示:
// Inside your test val dessertRepository = mock(DessertsRepository::class.java) launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment { // Test Fragment logic }
如要進一步瞭解這項測試的程序和完整範例,請參閱「測試片段」。