FragmentManager
adalah
class yang bertanggung jawab untuk melakukan tindakan pada fragmen aplikasi Anda, seperti
menambahkan, menghapus, atau menggantinya, dan menambahkannya ke data sebelumnya.
Anda mungkin tidak pernah berinteraksi dengan FragmentManager
secara langsung jika menggunakan
library Navigasi Jetpack, karena library tersebut berfungsi dengan
FragmentManager
atas nama Anda. Namun, aplikasi apa pun yang menggunakan
fragmen juga menggunakan FragmentManager
pada tingkat tertentu, jadi penting untuk memahami
arti dan cara kerjanya.
Halaman ini membahas:
- Cara mengakses
FragmentManager
. - Peran
FragmentManager
dalam kaitannya dengan aktivitas dan fragmen Anda. - Cara mengelola data sebelumnya dengan
FragmentManager
. - Cara menyediakan data dan dependensi ke fragmen Anda.
Mengakses FragmentManager
Anda dapat mengakses FragmentManager
dari aktivitas atau dari fragmen.
FragmentActivity
dan subclass-nya, seperti
AppCompatActivity
,
memiliki akses ke FragmentManager
melalui
metode
getSupportFragmentManager()
.
Fragmen dapat menghosting satu atau beberapa fragmen turunan. Dalam
fragmen, Anda bisa mendapatkan referensi ke FragmentManager
yang mengelola
turunan fragmen melalui
getChildFragmentManager()
.
Jika perlu mengakses FragmentManager
hostnya, Anda dapat
menggunakan getParentFragmentManager()
.
Berikut adalah beberapa contoh untuk melihat hubungan antara
fragmen, host-nya, dan instance FragmentManager
yang terkait
dengan masing-masing fragmen.
Gambar 1 menunjukkan dua contoh, yang masing-masing memiliki host aktivitas tunggal. Aktivitas
host di kedua contoh ini menampilkan navigasi level atas kepada
pengguna sebagai
BottomNavigationView
yang bertanggung jawab untuk menukar fragmen host dengan layar yang berbeda
dalam aplikasi. Setiap layar diterapkan sebagai fragmen terpisah.
Fragmen host di Contoh 1 menghosting dua fragmen turunan yang membentuk layar tampilan terpisah. Fragmen host di Contoh 2 menghosting fragmen turunan tunggal yang membentuk fragmen tampilan dari tampilan geser.
Dengan penyiapan ini, Anda dapat memikirkan setiap host sebagai FragmentManager
yang terkait dengannya yang mengelola fragmen turunannya. Hal ini diilustrasikan dalam
gambar 2, beserta pemetaan properti antara supportFragmentManager
,
parentFragmentManager
, dan childFragmentManager
.
Properti FragmentManager
yang sesuai untuk referensi tergantung pada lokasi
callsite dalam hierarki fragmen bersama dengan pengelola fragmen
yang Anda coba akses.
Setelah memiliki referensi ke FragmentManager
, Anda dapat
menggunakannya untuk memanipulasi fragmen yang ditampilkan kepada pengguna.
Fragmen turunan
Secara umum, aplikasi Anda terdiri atas satu atau sejumlah kecil
aktivitas dalam project aplikasi, dengan setiap aktivitas mewakili
sekelompok layar terkait. Aktivitas mungkin memberikan titik untuk menempatkan
navigasi tingkat atas dan tempat untuk mencakup objek ViewModel
dan status tampilan lainnya
di antara fragmen. Fragmen mewakili setiap tujuan di
aplikasi Anda.
Jika Anda ingin menampilkan beberapa fragmen sekaligus, seperti dalam tampilan terpisah atau dasbor, Anda dapat menggunakan fragmen turunan yang dikelola oleh fragmen tujuan dan pengelola fragmen turunannya.
Kasus penggunaan lainnya untuk fragmen turunan adalah sebagai berikut:
- Slide layar,
menggunakan
ViewPager2
di fragmen induk untuk mengelola serangkaian tampilan fragmen turunan. - Sub-navigasi dalam sekumpulan layar terkait.
- Navigasi Jetpack menggunakan fragmen turunan sebagai tujuan individu. Suatu
aktivitas menghosting satu
NavHostFragment
induk dan mengisi ruang dengan fragmen tujuan turunan yang berbeda saat pengguna menavigasi aplikasi Anda.
Menggunakan FragmentManager
FragmentManager
mengelola data sebelumnya pada fragmen. Pada runtime, FragmentManager
dapat menjalankan operasi data sebelumnya seperti menambahkan atau menghapus fragmen sebagai respons terhadap interaksi pengguna. Setiap kumpulan perubahan
disatukan sebagai unit tunggal yang disebut
FragmentTransaction
.
Untuk pembahasan lebih mendalam tentang transaksi fragmen, lihat
panduan transaksi fragmen.
Saat pengguna mengetuk tombol Kembali di perangkat, atau saat Anda memanggil
FragmentManager.popBackStack()
,
transaksi fragmen teratas akan muncul dari tumpukan. Jika tidak ada lagi transaksi fragmen
pada tumpukan, dan jika Anda tidak menggunakan fragmen turunan, peristiwa
Kembali akan muncul dalam aktivitas. Jika Anda menggunakan fragmen turunan, lihat
pertimbangan khusus untuk fragmen turunan dan yang setara.
Saat Anda memanggil
addToBackStack()
pada transaksi, transaksi tersebut dapat menyertakan sejumlah
operasi, seperti menambahkan beberapa fragmen atau mengganti fragmen dalam beberapa
penampung.
Saat data sebelumnya muncul, semua operasi ini
terbalik sebagai satu tindakan atomik. Namun, jika Anda melakukan
transaksi tambahan sebelum panggilan popBackStack()
, dan jika Anda
tidak menggunakan addToBackStack()
untuk transaksi tersebut, operasi ini
tidak terbalik. Oleh karena itu, dalam satu FragmentTransaction
, hindari
transaksi interleaving yang memengaruhi data sebelumnya dengan yang tidak.
Melakukan transaksi
Untuk menampilkan fragmen dalam penampung tata letak, gunakan FragmentManager
untuk membuat FragmentTransaction
. Dalam transaksi, selanjutnya Anda dapat
melakukan operasi
add()
atau replace()
pada penampung.
Misalnya, FragmentTransaction
sederhana mungkin terlihat seperti ini:
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();
Dalam contoh ini, ExampleFragment
menggantikan fragmen, jika ada,
yang saat ini ada di penampung tata letak yang diidentifikasi
ID R.id.fragment_container
. Dengan memberikan class fragmen ke metode
replace()
,
FragmentManager
dapat menangani pembuatan instance menggunakan
FragmentFactory
.
Untuk mengetahui informasi selengkapnya, lihat bagian Menyediakan dependensi untuk fragmen.
setReorderingAllowed(true)
mengoptimalkan perubahan status fragmen yang terlibat dalam transaksi
sehingga animasi dan transisi berfungsi dengan benar. Untuk informasi selengkapnya tentang
navigasi dengan animasi dan transisi, lihat
Transaksi fragmen dan
Menavigasi antara fragmen menggunakan animasi.
Memanggil
addToBackStack()
akan mengikat transaksi ke data sebelumnya. Nantinya pengguna dapat membalikkan
transaksi dan mengembalikan fragmen sebelumnya dengan mengetuk tombol
Kembali. Jika Anda menambahkan atau menghapus beberapa fragmen dalam satu
transaksi, semua operasi tersebut akan diurungkan saat data sebelumnya
muncul. Nama opsional yang diberikan dalam panggilan addToBackStack()
memberi
Anda kemampuan untuk memunculkan kembali transaksi spesifik tersebut menggunakan
popBackStack()
.
Jika Anda tidak memanggil addToBackStack()
saat melakukan transaksi yang
menghapus fragmen, fragmen yang dihapus akan dihancurkan saat
transaksi dilakukan, dan pengguna tidak dapat menavigasi kembali ke fragmen tersebut. Jika Anda
memanggil addToBackStack()
saat menghapus fragmen, fragmen tersebut
hanya STOPPED
dan RESUMED
setelahnya saat pengguna menavigasi kembali. Tampilannya
dihancurkan dalam hal ini. Untuk mengetahui informasi selengkapnya, lihat
Siklus proses fragmen
Menemukan fragmen yang ada
Anda dapat memperoleh referensi ke fragmen saat ini dalam penampung tata letak
dengan menggunakan
findFragmentById()
.
Gunakan findFragmentById()
untuk mencari fragmen baik dengan ID yang diberikan saat
di-inflate dari XML atau menurut ID penampung saat ditambahkan
di FragmentTransaction
. Berikut contohnya:
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);
Atau, Anda dapat menetapkan tag unik ke fragmen dan mendapatkan
referensi menggunakan
findFragmentByTag()
.
Anda dapat menetapkan tag menggunakan atribut XML android:tag
pada fragmen yang
ditentukan dalam tata letak, atau selama operasi add()
atau replace()
dalam FragmentTransaction
.
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");
Pertimbangan khusus untuk fragmen turunan dan yang seinduk
Hanya satu FragmentManager
yang dapat mengontrol data sebelumnya
di fragmen pada waktu tertentu. Jika aplikasi Anda menampilkan beberapa fragmen yang setara di
layar secara bersamaan, atau jika aplikasi Anda menggunakan fragmen turunan, satu
FragmentManager
ditetapkan untuk menangani navigasi utama aplikasi.
Untuk menentukan fragmen navigasi utama di dalam transaksi fragmen,
panggil metode
setPrimaryNavigationFragment()
pada transaksi, dengan meneruskan instance fragmen yang
childFragmentManager
-nya memiliki kontrol utama.
Pertimbangkan struktur navigasi sebagai serangkaian lapisan, dengan aktivitas sebagai lapisan terluar, yang menyelimuti setiap lapisan fragmen turunan di bawahnya. Setiap lapisan memiliki satu fragmen navigasi utama.
Saat peristiwa Kembali terjadi, lapisan terdalam mengontrol perilaku navigasi. Setelah lapisan terdalam tidak lagi memiliki transaksi fragmen untuk dimunculkan kembali, kontrol akan kembali ke lapisan berikutnya, dan proses ini terjadi berulang hingga Anda mencapai aktivitas tersebut.
Jika dua fragmen atau lebih ditampilkan secara bersamaan, hanya satu yang merupakan fragmen navigasi utama. Menetapkan fragmen sebagai fragmen navigasi utama akan menghapus penetapan dari fragmen sebelumnya. Menggunakan contoh sebelumnya, jika Anda menetapkan fragmen detail sebagai fragmen navigasi utama, penetapan fragmen utama akan dihapus.
Mendukung beberapa data sebelumnya
Dalam beberapa kasus, aplikasi Anda mungkin perlu mendukung beberapa data sebelumnya. Contoh yang umum
adalah jika aplikasi Anda menggunakan menu navigasi bawah. FragmentManager
memungkinkan
Anda mendukung beberapa data sebelumnya dengan metode saveBackStack()
dan
restoreBackStack()
. Metode ini memungkinkan Anda beralih antar-data
sebelumnya dengan menyimpan satu data sebelumnya dan memulihkan yang lain.
saveBackStack()
berfungsi mirip dengan memanggil popBackStack()
dengan parameter
name
opsional: transaksi yang ditentukan dan semua transaksi setelahnya di
stack akan muncul. Perbedaannya adalah saveBackStack()
menyimpan
status semua fragmen dalam transaksi
yang muncul.
Misalnya, sebelumnya Anda telah menambahkan fragmen ke data sebelumnya dengan
menyatukan FragmentTransaction
menggunakan addToBackStack()
, seperti yang ditunjukkan dalam
contoh berikut:
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();
Dalam hal ini, Anda dapat menyimpan transaksi fragmen ini dan status
ExampleFragment
dengan memanggil saveBackStack()
:
Kotlin
supportFragmentManager.saveBackStack("replacement")
Java
supportFragmentManager.saveBackStack("replacement");
Anda dapat memanggil restoreBackStack()
dengan parameter nama yang sama untuk memulihkan semua
transaksi yang muncul dan semua status fragmen yang disimpan:
Kotlin
supportFragmentManager.restoreBackStack("replacement")
Java
supportFragmentManager.restoreBackStack("replacement");
Memberikan dependensi ke fragmen Anda
Saat menambahkan fragmen, Anda dapat membuat instance fragmen secara manual dan
menambahkannya ke 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();
Saat Anda melakukan transaksi fragmen, instance fragmen
yang Anda buat adalah instance yang digunakan. Namun, selama
perubahan konfigurasi,
aktivitas Anda dan semua fragmennya akan dihancurkan, lalu dibuat ulang dengan
resource Android
yang paling sesuai.
FragmentManager
menangani semua ini untuk Anda: membuat ulang instance
fragmen, melampirkannya ke host, dan membuat ulang status data
sebelumnya.
Secara default, FragmentManager
menggunakan
FragmentFactory
yang
disediakan oleh framework untuk membuat instance baru fragmen Anda. Factory
default ini menggunakan refleksi untuk menemukan dan mengaktifkan konstruktor tanpa argumen
untuk fragmen Anda. Artinya Anda tidak dapat menggunakan factory default ini
untuk memberikan dependensi pada fragmen Anda. Hal ini juga berarti bahwa setiap konstruktor kustom
yang Anda gunakan untuk membuat fragmen pertama kali tidak digunakan
selama pembuatan ulang secara default.
Untuk memberikan dependensi pada fragmen Anda, atau untuk menggunakan konstruktor kustom,
buat subclass FragmentFactory
kustom,
lalu ganti
FragmentFactory.instantiate
.
Kemudian, Anda dapat mengganti factory default FragmentManager
dengan
factory kustom, yang kemudian digunakan untuk membuat instance fragmen.
Misalkan Anda memiliki DessertsFragment
yang bertanggung jawab untuk menampilkan
hidangan penutup populer di kota asal Anda, dan DessertsFragment
memiliki dependensi pada class DessertsRepository
yang memberinya
informasi yang diperlukan untuk menampilkan UI yang benar kepada pengguna.
Anda dapat menentukan DessertsFragment
untuk mewajibkan
instance DessertsRepository
dalam konstruktornya.
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. ... }
Penerapan sederhana FragmentFactory
Anda mungkin terlihat
seperti berikut ini.
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); } } }
Contoh ini menjadikan FragmentFactory
subclass, dengan menggantikan metode instantiate()
untuk memberikan logika pembuatan fragmen kustom untuk DessertsFragment
.
Class fragmen lainnya ditangani oleh perilaku default
FragmentFactory
hingga super.instantiate()
.
Kemudian, Anda dapat menetapkan MyFragmentFactory
sebagai factory yang akan digunakan saat
membuat fragmen aplikasi dengan menetapkan properti pada
FragmentManager
. Anda harus menetapkan properti ini sebelum super.onCreate()
aktivitas untuk memastikan bahwa MyFragmentFactory
digunakan saat
membuat ulang fragmen.
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); } }
Menetapkan FragmentFactory
dalam aktivitas akan menggantikan
pembuatan fragmen di seluruh hierarki fragmen aktivitas. Dengan kata lain, childFragmentManager
setiap fragmen turunan yang Anda tambahkan menggunakan factory fragmen kustom yang ditetapkan di sini, kecuali jika diganti pada level yang lebih rendah.
Menguji dengan FragmentFactory
Dalam satu arsitektur aktivitas, uji fragmen
Anda secara terpisah menggunakan
class
FragmentScenario
. Karena Anda tidak dapat mengandalkan logika onCreate
kustom dari
aktivitas, Anda dapat meneruskan FragmentFactory
sebagai argumen
ke pengujian fragmen, seperti ditunjukkan dalam contoh berikut:
// Inside your test val dessertRepository = mock(DessertsRepository::class.java) launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment { // Test Fragment logic }
Untuk informasi mendetail tentang proses pengujian ini dan contoh lengkapnya, lihat Menguji fragmen Anda.