Google berkomitmen untuk mendorong terwujudnya keadilan rasial bagi komunitas Kulit Hitam. Lihat caranya.

Fragmen   Part of Android Jetpack.

Fragment mewakili perilaku atau bagian dari antarmuka pengguna dalam FragmentActivity. Anda bisa mengombinasikan beberapa fragmen dalam satu aktivitas untuk membangun UI multipanel dan menggunakan kembali sebuah fragmen dalam beberapa aktivitas. Anda bisa menganggap fragmen sebagai bagian modular dari aktivitas, yang memiliki daur hidup sendiri, menerima kejadian masukan sendiri, dan yang bisa Anda tambahkan atau hapus saat aktivitas berjalan (semacam "subaktivitas" yang bisa digunakan kembali dalam aktivitas berbeda).

Fragmen harus selalu tersemat dalam aktivitas dan daur hidup fragmen secara langsung dipengaruhi oleh daur hidup aktivitas host-nya. Misalnya, saat aktivitas dihentikan sementara, semua fragmen di dalamnya juga dihentikan sementara, dan bila aktivitas dimusnahkan, semua fragmen juga demikian. Akan tetapi, saat aktivitas berjalan (dalam status daur hidup dilanjutkan), Anda bisa memanipulasi setiap fragmen secara terpisah, seperti menambah atau membuangnya. Saat melakukan transaksi fragmen, Anda juga bisa menambahkannya ke back-stack yang dikelola oleh aktivitas—setiap entri back-stack merupakan catatan transaksi fragmen yang terjadi. Dengan back-stack pengguna dapat membalikkan transaksi fragmen (mengarah mundur), dengan menekan tombol Kembali.

Bila Anda menambahkan fragmen sebagai bagian dari layout aktivitas, fragmen tersebut berada di ViewGroup di dalam hierarki tampilan aktivitas dan fragmen menentukan layout tampilannya sendiri. Anda bisa menyisipkan fragmen ke dalam layout aktivitas dengan mendeklarasikan fragmen dalam file layout aktivitas, sebagai elemen <fragment>, atau dari kode aplikasi Anda dengan menambahkannya ke ViewGroup yang ada.

Dokumen ini menjelaskan cara membuat aplikasi menggunakan fragmen, termasuk cara fragmen mempertahankan statusnya bila ditambahkan ke back-stack aktivitas, berbagi kejadian dengan aktivitas, dan fragmen lain dalam aktivitas, berkontribusi pada bilah aksi aktivitas, dan lainnya.

Untuk informasi tentang menangani daur hidup, termasuk panduan tentang praktik terbaik, lihat resource berikut:

Filosofi Desain

Android memperkenalkan fragmen di Android 3.0 (API level 11), terutama untuk mendukung desain UI yang lebih dinamis dan fleksibel pada layar besar, seperti tablet. Karena layar tablet jauh lebih besar daripada layar handset, maka lebih banyak ruang untuk mengombinasikan dan bertukar komponen UI. Fragmen memungkinkan desain seperti itu tanpa perlu mengelola perubahan kompleks pada hierarki tampilan. Dengan membagi layout aktivitas menjadi beberapa fragmen, Anda bisa mengubah penampilan aktivitas saat waktu proses dan mempertahankan perubahan itu di back-stack yang dikelola oleh aktivitas. Mode-mode tersebut kini tersedia secara luas melalui library dukungan fragmen.

Misalnya, aplikasi berita bisa menggunakan satu fragmen untuk menampilkan daftar artikel di sebelah kiri dan fragmen lainnya untuk menampilkan artikel di sebelah kanan—kedua fragmen ini muncul di satu aktivitas, berdampingan, dan masing-masing fragmen memiliki serangkaian metode callback daur hidup dan menangani kejadian masukan penggunanya sendiri. Sehingga, sebagai ganti menggunakan satu aktivitas untuk memilih artikel dan aktivitas lainnya untuk membaca artikel, pengguna bisa memilih artikel dan membaca semuanya dalam aktivitas yang sama, sebagaimana diilustrasikan dalam layout tablet pada gambar 1.

Anda harus mendesain masing-masing fragmen sebagai komponen aktivitas modular dan bisa digunakan kembali. Yakni, karena setiap fragmen mendefinisikan layoutnya dan perilakunya dengan callback daur hidupnya sendiri, Anda bisa memasukkan satu fragmen dalam banyak aktivitas, sehingga Anda harus mendesainnya untuk digunakan kembali dan mencegah memanipulasi satu fragmen dari fragmen lain secara langsung. Ini terutama penting karena dengan fragmen modular Anda bisa mengubah kombinasi fragmen untuk ukuran layar yang berbeda. Saat mendesain aplikasi untuk mendukung tablet maupun handset, Anda bisa menggunakan kembali fragmen dalam konfigurasi layout yang berbeda untuk mengoptimalkan pengalaman pengguna berdasarkan ruang layar yang tersedia. Misalnya, pada handset, fragmen mungkin perlu dipisahkan untuk menyediakan UI panel tunggal bila lebih dari satu yang tidak cocok dalam aktivitas yang sama.

Gambar 1. Contoh bagaimana dua modul UI yang ditentukan oleh fragmen bisa digabungkan ke dalam satu aktivitas untuk desain tablet, namun dipisahkan untuk desain handset.

Misalnya—untuk melanjutkan contoh aplikasi berita—aplikasi bisa menyematkan dua fragmen dalam Aktivitas A, saat berjalan pada perangkat berukuran tablet. Akan tetapi, pada layar berukuran handset, ruang untuk kedua fragmen tidak cukup, sehingga Aktivitas A hanya menyertakan fragmen untuk daftar artikel, dan saat pengguna memilih artikel, Aktivitas B akan dimulai, termasuk fragmen kedua untuk membaca artikel. Sehingga, aplikasi mendukung tablet dan handset dengan menggunakan kembali fragmen dalam kombinasi berbeda, seperti diilustrasikan dalam gambar 1.

Untuk informasi selengkapnya tentang mendesain aplikasi menggunakan kombinasi fragmen berbeda untuk konfigurasi layar yang berbeda, lihat panduan untuk Mendukung Tablet dan Handset.

Membuat Fragmen

Gambar 2. Daur hidup fragmen (saat aktivitasnya berjalan).

Untuk membuat fragmen, Anda harus membuat subclass Fragment (atau subclass-nya yang ada). Class Fragment memiliki kode yang mirip seperti Activity. Class ini memiliki metode callback yang serupa dengan aktivitas, seperti onCreate(), onStart(), onPause(), dan onStop(). Sebenarnya, jika Anda mengonversi aplikasi Android saat ini untuk menggunakan fragmen, Anda mungkin cukup memindahkan kode dari metode callback aktivitas ke masing-masing metode callback fragmen.

Biasanya, Anda harus mengimplementasikan setidaknya metode daur hidup berikut ini:

onCreate()
Sistem akan memanggilnya saat membuat fragmen. Dalam implementasi, Anda harus melakukan inisialisasi komponen penting dari fragmen yang ingin dipertahankan saat fragmen dihentikan sementara atau dihentikan, kemudian dilanjutkan.
onCreateView()
Sistem akan memanggilnya saat fragmen menggambar antarmuka penggunanya untuk yang pertama kali. Untuk menggambar UI fragmen, Anda harus mengembalikan View dari metode ini yang menjadi root layout fragmen. Hasil yang dikembalikan bisa berupa null jika fragmen tidak menyediakan UI.
onPause()
Sistem akan memanggil metode ini sebagai indikasi pertama bahwa pengguna sedang meninggalkan fragmen Anda (walau itu tidak selalu berarti fragmen sedang dimusnahkan). Di sinilah biasanya Anda harus mengikat perubahan yang harus dipertahankan di luar sesi pengguna saat ini (karena pengguna mungkin tidak akan kembali).

Kebanyakan aplikasi harus mengimplementasikan setidaknya tiga metode ini untuk setiap fragmen, tetapi ada beberapa metode callback lain yang juga harus Anda gunakan untuk menangani berbagai tahap daur hidup fragmen. Semua metode callback daur hidup akan dibahas secara lebih detail, di bagian tentang Menangani Daur Hidup Fragmen.

Perhatikan bahwa kode yang mengimplementasikan aksi daur hidup dari komponen dependen harus ditempatkan di komponen itu sendiri, bukan dalam implementasi callback fragmen. Lihat Menangani Daur Hidup dengan Komponen Lifecycle-Aware untuk mempelajari cara membuat komponen lifecyle-aware dependen.

Ada juga beberapa subclass yang mungkin perlu diperpanjang, sebagai ganti class basis Fragment:

DialogFragment
Menampilkan dialog mengambang. Penggunaan class ini untuk membuat dialog merupakan alternatif yang baik dari penggunaan metode helper dialog di class Activity, karena Anda bisa menyatukan dialog fragmen ke dalam back-stack fragmen yang dikelola oleh aktivitas, sehingga pengguna bisa menutup ke fragmen yang ditinggalkan.
ListFragment
Menampilkan daftar item yang dikelola oleh adaptor (misalnya SimpleCursorAdapter), serupa dengan ListActivity. Menampilkan beberapa metode pengelolaan tampilan daftar, seperti callback onListItemClick() untuk menangani kejadian klik. (Perhatikan bahwa metode yang disukai untuk menampilkan daftar adalah dengan menggunakan RecyclerView dan bukan ListView. Dalam hal ini Anda akan perlu membuat fragmen yang menyertakan RecyclerView dalam layout-nya. Lihat Buat Daftar dengan RecyclerView untuk mempelajari caranya.)
PreferenceFragmentCompat
Menampilkan hierarki objek Preference sebagai daftar. Proses ini digunakan untuk membuat layar setelan untuk aplikasi Anda.

Menambahkan antarmuka pengguna

Fragmen biasanya digunakan sebagai bagian dari antarmuka pengguna aktivitas dan menyumbangkan layoutnya sendiri ke aktivitas.

Untuk menyediakan layout fragmen, Anda harus mengimplementasikan metode callback onCreateView(), yang dipanggil sistem Android bila tiba saatnya fragmen menggambar layoutnya. Implementasi Anda atas metode ini harus mengembalikan View yang menjadi root layout fragmen.

Catatan: Jika fragmen adalah subclass ListFragment, implementasi default akan mengembalikan ListView dari onCreateView(), sehingga Anda tidak perlu mengimplementasikannya.

Untuk mengembalikan layout dari onCreateView(), Anda bisa memekarkannya dari resource layout yang ditentukan di XML. Untuk membantu melakukannya, onCreateView() menyediakan objek LayoutInflater.

Misalnya, inilah subclass Fragment yang memuat layout dari file example_fragment.xml:

Kotlin

class ExampleFragment : Fragment() {

    override fun onCreateView(
            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false)
    }
}

Java

public static class ExampleFragment extends Fragment {
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        return inflater.inflate(R.layout.example_fragment, container, false);
    }
}

Catatan: Dalam contoh di atas, R.layout.example_fragment merupakan referensi ke sumber daya layout bernama example_fragment.xml yang tersimpan dalam sumber daya aplikasi. Untuk informasi tentang cara membuat layout di XML, lihat dokumentasi Antarmuka Pengguna.

Parameter container yang diteruskan ke onCreateView() adalah induk ViewGroup (dari layout aktivitas) tempat layout fragmen akan disisipkan. Parameter savedInstanceState adalah Bundle yang menyediakan data tentang instance fragmen sebelumnya, jika fragmen dilanjutkan (status pemulihan dibahas selengkapnya di bagian tentang Menangani Daur Hidup Fragmen).

Metode inflate() mengambil tiga argumen:

  • ID sumber daya layout yang ingin dimekarkan.
  • ViewGroup akan menjadi induk dari layout yang dimekarkan. container perlu diteruskan agar sistem menerapkan parameter layout ke tampilan akar layout yang dimekarkan, yang ditetapkan dalam tampilan induk yang akan dituju.
  • Boolean yang menunjukkan apakah layout yang dimekarkan harus dilampirkan ke ViewGroup (parameter kedua) selama pemekaran. (Dalam hal ini, ini salah karena sistem sudah memasukkan layout yang dimekarkan ke dalam container—meneruskan true akan membuat tampilan grup berlebih dalam layout akhir.)

Anda kini telah melihat cara membuat fragmen yang menyediakan layout. Berikutnya, Anda perlu menambahkan fragmen ke aktivitas.

Menambahkan fragmen ke aktivitas

Biasanya, fragmen berkontribusi pada sebagian UI ke aktivitas host, yang disematkan sebagai bagian dari hierarki tampilan keseluruhan aktivitas. Ada dua cara untuk menambahkan fragmen ke layout aktivitas:

  • Deklarasikan fragmen dalam file layout aktivitas.

    Dalam hal ini, Anda bisa menetapkan properti layout fragmen seakan-akan sebuah tampilan. Misalnya, berikut ini adalah file layout untuk aktivitas dengan dua fragmen:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="horizontal"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        <fragment android:name="com.example.news.ArticleListFragment"
                android:id="@+id/list"
                android:layout_weight="1"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
        <fragment android:name="com.example.news.ArticleReaderFragment"
                android:id="@+id/viewer"
                android:layout_weight="2"
                android:layout_width="0dp"
                android:layout_height="match_parent" />
    </LinearLayout>
    

    Atribut android:name dalam <fragment> menetapkan class Fragment untuk dibuat instance-nya dalam layout.

    Saat sistem membuat layout aktivitas, sistem membuat instance setiap fragmen sebagaimana yang ditetapkan dalam layout dan memanggil metode onCreateView() masing-masing, untuk mengambil setiap fragmen. Sistem akan menyisipkan View yang dikembalikan oleh fragmen secara langsung, menggantikan elemen <fragment>.

    Catatan: Setiap fragmen memerlukan identifier unik yang bisa digunakan sistem untuk memulihkan fragmen jika aktivitas dimulai kembali (dan identifier yang bisa digunakan menangkap fragmen untuk melakukan transaksi, seperti membuangnya). Ada dua cara untuk memberikan ID bagi fragmen:

    • Menyediakan atribut android:id bersama ID unik.
    • Menyediakan atribut android:tag bersama string unik.
  • Atau, secara programatis tambahkan fragmen ke ViewGroup yang ada.

    Kapan saja saat aktivitas berjalan, Anda bisa menambahkan fragmen ke layout aktivitas. Anda cukup menetapkan ViewGroup di tempat memasukkan fragmen.

    Untuk membuat transaksi fragmen dalam aktivitas (seperti menambah, membuang, atau mengganti fragmen), Anda harus menggunakan API dari FragmentTransaction. Anda bisa mengambil instance FragmentTransaction dari FragmentActivity seperti ini:

    Kotlin

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

    Java

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

    Selanjutnya Anda bisa menambahkan fragmen menggunakan metode add(), dengan menetapkan fragmen yang akan ditambahkan dan tampilan tempat menyisipkannya. Sebagai contoh:

    Kotlin

    val fragment = ExampleFragment()
    fragmentTransaction.add(R.id.fragment_container, fragment)
    fragmentTransaction.commit()
    

    Java

    ExampleFragment fragment = new ExampleFragment();
    fragmentTransaction.add(R.id.fragment_container, fragment);
    fragmentTransaction.commit();
    

    Argumen pertama yang diteruskan ke add() adalah ViewGroup tempat fragmen harus dimasukkan, yang ditetapkan oleh ID resource, dan parameter kedua merupakan fragmen yang akan ditambahkan.

    Setelah membuat perubahan dengan FragmentTransaction, Anda harus memanggil commit() untuk menerapkan perubahan.

Mengelola Fragmen

Untuk mengelola fragmen dalam aktivitas, Anda perlu menggunakan FragmentManager. Untuk mendapatkannya, panggil getSupportFragmentManager() dari aktivitas Anda.

Beberapa hal yang dapat Anda lakukan dengan FragmentManager antara lain:

Untuk informasi selengkapnya tentang metode ini dan hal lainnya, lihat dokumentasi class FragmentManager.

Seperti yang ditunjukkan di bagian sebelumnya, Anda juga bisa menggunakan FragmentManager untuk membuka FragmentTransaction, sehingga Anda bisa melakukan transaksi, seperti menambah dan membuang fragmen.

Melakukan Transaksi Fragmen

Fitur menarik terkait penggunaan fragmen di aktivitas adalah kemampuan menambah, membuang, mengganti, dan melakukan tindakan lain dengannya, sebagai respons atas interaksi pengguna. Setiap set perubahan yang Anda lakukan untuk aktivitas disebut transaksi dan Anda bisa melakukan transaksi menggunakan API di FragmentTransaction. Anda juga bisa menyimpan setiap transaksi ke back-stack yang dikelola aktivitas, sehingga pengguna bisa mengarah mundur melalui perubahan fragmen (mirip mengarah mundur melalui aktivitas).

Anda bisa memperoleh instance FragmentTransaction dari FragmentManager seperti ini:

Kotlin

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

Java

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

Setiap transaksi merupakan serangkaian perubahan yang ingin dilakukan pada waktu yang sama. Anda bisa menyiapkan semua perubahan yang ingin dilakukan untuk transaksi mana saja menggunakan metode seperti add(), remove(), dan replace(). Kemudian, untuk menerapkan transaksi pada aktivitas, Anda harus memanggil commit().

Akan tetapi, sebelum memanggil commit(), Anda mungkin perlu memanggil addToBackStack(), untuk menambahkan transaksi ke back-stack transaksi fragmen. Back-stack ini dikelola oleh aktivitas dan memungkinkan pengguna kembali ke status fragmen sebelumnya, dengan menekan tombol Kembali.

Misalnya, dengan cara ini Anda bisa mengganti satu fragmen dengan yang fragmen lain, dan mempertahankan status sebelumnya di back-stack:

Kotlin

val newFragment = ExampleFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragment_container, newFragment)
transaction.addToBackStack(null)
transaction.commit()

Java

// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();

// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);

// Commit the transaction
transaction.commit();

Dalam contoh ini, newFragment menggantikan fragmen apa saja (jika ada) yang saat ini berada dalam kontainer layout yang diidentifikasi melalui ID R.id.fragment_container. Dengan memanggil addToBackStack(), transaksi yang diganti disimpan ke back-stack sehingga pengguna bisa membalikkan transaksi dan mengembalikan fragmen sebelumnya dengan menekan tombol Kembali.

FragmentActivity lalu secara otomatis mengambil fragmen dari back-stack melalui onBackPressed().

Jika Anda menambahkan beberapa perubahan pada transaksi—seperti add() atau remove()—dan memanggil addToBackStack(), maka semua perubahan yang diterapkan sebelum Anda memanggil commit() akan ditambahkan ke back-stack sebagai transaksi tunggal dan tombol Kembali akan membalikkannya bersama-sama.

Urutan menambahkan perubahan pada FragmentTransaction tidak berpengaruh, kecuali:

  • Anda harus memanggil commit() paling akhir.
  • Jika Anda menambahkan beberapa fragmen ke container yang sama, maka urutan penambahannya akan menentukan urutan munculnya dalam hierarki tampilan.

Jika Anda tidak memanggil addToBackStack() saat melakukan transaksi yang membuang fragmen, maka fragmen itu akan dimusnahkan bila transaksi diikat dan pengguna tidak bisa mengarah kembali ke sana. Sedangkan, jika Anda memanggil addToBackStack() saat menghapus fragmen, maka fragmen itu akan dihentikan dan nanti dilanjutkan jika pengguna mengarah kembali.

Tips: Untuk setiap transaksi fragmen, Anda bisa menerapkan animasi transisi, dengan memanggil setTransition() sebelum mengikatnya.

Memanggil commit() tidak langsung menjalankan transaksi. Namun sebuah jadwal akan dibuat untuk dijalankan pada thread UI aktivitas (thread "utama") begitu thread bisa melakukannya. Akan tetapi, jika perlu Anda bisa memanggil executePendingTransactions() dari thread UI untuk segera mengeksekusi transaksi yang diserahkan oleh commit(). Hal itu biasanya tidak perlu kecuali jika transaksi merupakan dependensi bagi tugas dalam thread lain.

Perhatian: Anda bisa mengikat transaksi menggunakan commit() hanya sebelum aktivitas menyimpan statusnya (bila pengguna meninggalkan aktivitas). Jika Anda mencoba mengikatnya setelah itu, pengecualian akan dilontarkan. Ini karena status setelah pengikatan bisa hilang jika aktivitas perlu dipulihkan. Untuk situasi yang memperbolehkan Anda meniadakan pengikatan (commit), gunakan commitAllowingStateLoss().

Berkomunikasi dengan Aktivitas

Meskipun Fragment diimplementasikan sebagai objek yang tidak bergantung pada FragmentActivity dan bisa digunakan dalam banyak aktivitas, instance tertentu dari fragmen secara langsung terkait dengan aktivitas yang menjadi hostnya.

Khususnya, fragmen bisa mengakses instance FragmentActivity dengan getActivity() dan dengan mudah melakukan tugas-tugas seperti mencari tampilan dalam layout aktivitas:

Kotlin

val listView: View? = activity?.findViewById(R.id.list)

Java

View listView = getActivity().findViewById(R.id.list);

Demikian pula, aktivitas Anda bisa memanggil metode di fragmen dengan mendapatkan referensi ke Fragment dari FragmentManager, menggunakan findFragmentById() atau findFragmentByTag(). Sebagai contoh:

Kotlin

val fragment = supportFragmentManager.findFragmentById(R.id.example_fragment) as ExampleFragment

Java

ExampleFragment fragment = (ExampleFragment) getSupportFragmentManager().findFragmentById(R.id.example_fragment);

Membuat callback kejadian pada aktivitas

Dalam beberapa kasus, Anda mungkin perlu fragmen untuk membagikan kejadian atau data dengan aktivitas dan/atau fragmen lain yang di-host oleh aktivitas. Untuk membagikan data, buat ViewModel bersama, seperti diuraikan dalam Membagikan data antar bagian fragmen di panduan ViewModel. Jika harus menyebarkan kejadian yang tidak dapat ditangani dengan ViewModel, Anda dapat mendefinisikan antarmuka callback di dalam fragment dan mengharuskan kejadian host menerapkannya. Saat aktivitas menerima callback melalui antarmuka, aktivitas akan bisa berbagi informasi itu dengan fragmen lain dalam layout jika perlu.

Misalnya, jika sebuah aplikasi berita memiliki dua fragmen dalam aktivitas—satu untuk menampilkan daftar artikel (fragmen A) dan satu lagi untuk menampilkan artikel (fragmen B)—maka fragmen A harus memberi tahu aktivitas bila item daftar dipilih sehingga aktivitas bisa memberi tahu fragmen B untuk menampilkan artikel. Dalam hal ini, antarmuka OnArticleSelectedListener dideklarasikan di dalam fragmen A:

Kotlin

public class FragmentA : ListFragment() {
    ...
    // Container Activity must implement this interface
    interface OnArticleSelectedListener {
        fun onArticleSelected(articleUri: Uri)
    }
    ...
}

Java

public static class FragmentA extends ListFragment {
    ...
    // Container Activity must implement this interface
    public interface OnArticleSelectedListener {
        public void onArticleSelected(Uri articleUri);
    }
    ...
}

Selanjutnya aktivitas yang menjadi host fragmen akan mengimplementasikan antarmuka OnArticleSelectedListener dan menggantikan onArticleSelected() untuk memberi tahu fragmen B mengenai kejadian dari fragmen A. Untuk memastikan bahwa aktivitas host mengimplementasikan antarmuka ini, metode callback fragmen A onAttach() (yang dipanggil sistem saat menambahkan fragmen ke aktivitas) membuat instance OnArticleSelectedListener dengan membuat Activity yang diteruskan ke onAttach():

Kotlin

public class FragmentA : ListFragment() {

    var listener: OnArticleSelectedListener? = null
    ...
    override fun onAttach(context: Context) {
        super.onAttach(context)
        listener = context as? OnArticleSelectedListener
        if (listener == null) {
            throw ClassCastException("$context must implement OnArticleSelectedListener")
        }

    }
    ...
}

Java

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener listener;
    ...
    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        try {
            listener = (OnArticleSelectedListener) context;
        } catch (ClassCastException e) {
            throw new ClassCastException(context.toString() + " must implement OnArticleSelectedListener");
        }
    }
    ...
}

Jika aktivitas belum mengimplementasikan antarmuka, maka fragmen akan melontarkan ClassCastException. Jika berhasil, anggota mListener yang menyimpan referensi ke implementasi aktivitas OnArticleSelectedListener, sehingga fragmen A bisa berbagi kejadian dengan aktivitas, dengan memanggil metode yang didefinisikan oleh antarmuka OnArticleSelectedListener. Misalnya, jika fragmen A adalah ekstensi dari ListFragment, maka setiap kali pengguna mengklik item daftar, sistem akan memanggil onListItemClick() di fragmen, yang selanjutnya memanggil onArticleSelected() untuk berbagi kejadian dengan aktivitas:

Kotlin

public class FragmentA : ListFragment() {

    var listener: OnArticleSelectedListener? = null
    ...
    override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) {
        // Append the clicked item's row ID with the content provider Uri
        val noteUri: Uri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id)
        // Send the event and Uri to the host activity
        listener?.onArticleSelected(noteUri)
    }
    ...
}

Java

public static class FragmentA extends ListFragment {
    OnArticleSelectedListener listener;
    ...
    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Append the clicked item's row ID with the content provider Uri
        Uri noteUri = ContentUris.withAppendedId(ArticleColumns.CONTENT_URI, id);
        // Send the event and Uri to the host activity
        listener.onArticleSelected(noteUri);
    }
    ...
}

Parameter id yang diteruskan ke onListItemClick() merupakan ID baris dari item yang diklik, yang digunakan aktivitas (atau fragmen lain) untuk mengambil artikel dari ContentProvider aplikasi.

Informasi selengkapnya tentang menggunakan penyedia materi tersedia dalam dokumen Penyedia Konten.

Menambahkan item ke Bilah Aplikasi

Fragmen Anda bisa menyumbangkan item menu ke Menu Opsi aktivitas (dan, konsekuensinya, bilah aplikasi) dengan mengimplementasikan onCreateOptionsMenu(). Agar metode ini bisa menerima panggilan, Anda harus memanggil setHasOptionsMenu() selama onCreate(), untuk menunjukkan bahwa fragmen ingin menambahkan item ke Menu Opsi. Jika tidak, fragmen tidak menerima panggilan ke onCreateOptionsMenu().

Setiap item yang selanjutnya Anda tambahkan ke Menu Opsi dari fragmen akan ditambahkan ke item menu yang ada. Fragmen juga menerima callback ke onOptionsItemSelected() bila item menu dipilih.

Anda juga bisa mendaftarkan tampilan dalam layout fragmen untuk menyediakan menu konteks dengan memanggil registerForContextMenu(). Bila pengguna membuka menu konteks, fragmen akan menerima panggilan ke onCreateContextMenu(). Bila pengguna memilih item, fragmen akan menerima panggilan ke onContextItemSelected().

Catatan: Walaupun fragmen menerima callback pada item yang dipilih untuk setiap item menu yang ditambahkannya, aktivitaslah yang pertama kali menerima masing-masing callback saat pengguna memilih item menu. Jika implementasi aktivitas dari callback bila-item-dipilih tidak menangani item yang dipilih, maka kejadian akan diteruskan ke callback fragmen. Ini berlaku untuk Menu Opsi dan menu konteks.

Untuk informasi selengkapnya tentang menu, lihat panduan developer untuk Menu dan kelas pelatihan Bilah Aplikasi.

Menangani Daur Hidup Fragmen

Gambar 3. Efek daur hidup aktivitas pada daur hidup fragmen.

Mengelola daur hidup fragmen mirip sekali dengan mengelola daur hidup aktivitas. Seperti aktivitas, fragmen bisa berada dalam tiga status:

Dilanjutkan
Fragmen terlihat dalam aktivitas yang berjalan.
Dihentikan sementara
Aktivitas lain berada di latar depan dan memiliki fokus, namun aktivitas tempat fragmen berada masih terlihat (aktivitas latar depan sebagian terlihat atau tidak menutupi seluruh layar).
Dihentikan
Fragment tidak terlihat. Aktivitas host telah dihentikan atau fragmen telah dihapus dari aktivitas namun ditambahkan ke back-stack. Fragmen yang dihentikan masih hidup (semua status dan informasi anggota masih disimpan oleh sistem). Akan tetapi, fragmen tidak terlihat lagi oleh pengguna dan akan dimatikan jika aktivitas dimatikan.

Seperti halnya aktivitas, Anda dapat mempertahankan status UI fragment di seluruh perubahan konfigurasi dan kematian proses menggunakan kombinasi onSaveInstanceState(Bundle), ViewModel, serta penyimpanan lokal persisten. Untuk mengetahui selengkapnya tentang mempertahankan status UI, lihat Menyimpan Status UI.

Perbedaan paling signifikan dalam daur hidup antara aktivitas dan fragmen ada pada cara penyimpanannya dalam back-stack masing-masing. Aktivitas ditempatkan ke dalam back-stack aktivitas yang dikelola oleh sistem saat dihentikan, secara default (sehingga pengguna bisa mengarah kembali ke aktivitas dengan tombol Kembali, seperti yang dibahas dalam Tugas dan Back-Stack). Namun, fragmen ditempatkan ke dalam back-stack yang dikelola oleh aktivitas host hanya jika Anda secara eksplisit meminta instance tersebut disimpan dengan memanggil addToBackStack() selama transaksi yang menghapus segmen tersebut.

Jika tidak, pengelolaan daur hidup fragmen mirip sekali dengan mengelola daur hidup aktivitas; berlaku praktik yang sama. Lihat panduan Daur Hidup Aktivitas dan Menangani Daur Hidup dengan Komponen Lifecycle-Aware untuk mempelajari lebih lanjut tentang daur hidup aktivitas dan praktik untuk mengelolanya.

Perhatian: Jika Anda memerlukan objek Context di dalam Fragment, Anda dapat memanggil getContext(). Akan tetapi, berhati-hatilah untuk memanggil getContext() hanya bila fragmen telah dikaitkan dengan aktivitas. Apabila fragmen belum terkait dengan, getContext() mengembalikan null.

Mengoordinasi dengan daur hidup aktivitas

Daur hidup aktivitas tempat fragmen berada akan memengaruhi secara langsung siklus hidup fragmen sedemikian rupa sehingga setiap callback daur hidup aktivitas menghasilkan callback yang sama untuk masing-masing fragmen. Misalnya, bila aktivitas menerima onPause(), maka masing-masing fragmen dalam aktivitas akan menerima onPause().

Namun fragmen memiliki beberapa callback daur hidup ekstra, yang menangani interaksi unik dengan aktivitas untuk melakukan tindakan seperti membangun dan memusnahkan UI fragmen. Metode callback tambahan ini adalah:

onAttach()
Dipanggil bila fragmen telah dikaitkan dengan aktivitas (Activity diteruskan di sini).
onCreateView()
Dipanggil untuk membuat hierarki tampilan yang dikaitkan dengan fragmen.
onActivityCreated()
Dipanggil bila metode onCreate() aktivitas telah dikembalikan.
onDestroyView()
Dipanggil bila hierarki tampilan yang terkait dengan fragmen dihapus.
onDetach()
Dipanggil bila fragmen diputuskan dari aktivitas.

Alur daur hidup fragmen, karena dipengaruhi oleh aktivitas host-nya, diilustrasikan oleh gambar 3. Dalam gambar ini, Anda bisa melihat bagaimana setiap status aktivitas yang berurutan menentukan metode callback mana yang mungkin diterima fragmen. Misalnya, saat aktivitas menerima callback onCreate(), fragmen dalam aktivitas akan menerima tidak lebih dari callback onActivityCreated().

Setelah status aktivitas diteruskan kembali, Anda bisa bebas menambah dan membuang fragmen untuk aktivitas tersebut. Sehingga, hanya saat aktivitas berada dalam status dilanjutkan, daur hidup fragmen bisa berubah secara independen.

Akan tetapi, saat aktivitas meninggalkan status dilanjutkan, fragmen akan kembali didorong melalui daur hidupnya oleh aktivitas.

Contoh

Untuk merangkum semua yang telah dibahas dalam dokumen ini, berikut ini contoh aktivitas yang menggunakan dua fragmen untuk membuat layout dua panel. Aktivitas di bawah ini menyertakan satu fragmen untuk menampilkan daftar putar Shakespeare dan fragmen lainnya menampilkan rangkuman pemutaran bila dipilih dari daftar. Aktivitas ini juga menunjukkan cara menyediakan konfigurasi fragmen berbeda, berdasarkan konfigurasi layar.

Catatan: Kode sumber lengkap untuk aktivitas ini tersedia di aplikasi contoh yang menunjukkan penggunaan class FragmentLayout contoh.

Aktivitas utama akan menerapkan layout seperti biasa, selama onCreate():

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)

    setContentView(R.layout.fragment_layout)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    setContentView(R.layout.fragment_layout);
}

Layout yang diterapkan adalah fragment_layout.xml:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent" android:layout_height="match_parent">

    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
            android:id="@+id/titles" android:layout_weight="1"
            android:layout_width="0px" android:layout_height="match_parent" />

    <FrameLayout android:id="@+id/details" android:layout_weight="1"
            android:layout_width="0px" android:layout_height="match_parent"
            android:background="?android:attr/detailsElementBackground" />

</LinearLayout>

Dengan layout ini, sistem akan membuat instance TitlesFragment (yang mencantumkan judul) begitu aktivitas memuat layout, sementara FrameLayout (lokasi penempatan fragmen untuk menampilkan rangkuman pemutaran) menempati ruang di sisi kanan layar, namun pada awalnya masih kosong. Seperti yang akan Anda lihat di bawah ini, sampai pengguna memilih item dari daftar maka fragmen baru akan ditempatkan ke dalam FrameLayout.

Namun, tidak semua konfigurasi layar cukup lebar untuk menampilkan daftar putar dan rangkuman secara berdampingan. Jadi, layout di atas hanya digunakan untuk konfigurasi layar lanskap, dengan menyimpannya di res/layout-land/fragment_layout.xml.

Sehingga, bila layar berada dalam orientasi potret, sistem akan menerapkan layout berikut, yang tersimpan di res/layout/fragment_layout.xml:

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent" android:layout_height="match_parent">
    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"
            android:id="@+id/titles"
            android:layout_width="match_parent" android:layout_height="match_parent" />
</FrameLayout>

Layout ini hanya menyertakan TitlesFragment. Ini artinya saat perangkat berada dalam orientasi potret, hanya judul daftar putar yang terlihat. Jadi, saat pengguna mengklik item daftar dalam konfigurasi ini, aplikasi memulai aktivitas baru untuk menampilkan rangkuman, sebagai ganti pemuatan fragmen kedua.

Berikutnya, Anda bisa melihat bagaimana hal ini dilakukan dalam class fragmen. Pertama adalah TitlesFragment, yang menampilkan judul pemutaran Shakespeare. Fragmen ini membuat ekstensi ListFragment dan mengandalkannya untuk menangani sebagian besar pekerjaan tampilan daftar.

Saat Anda memeriksa kode ini, perhatikan bahwa ada dua kemungkinan perilaku saat pengguna mengklik item daftar: bergantung pada layout yang aktif, bisa membuat dan menampilkan fragmen baru untuk menampilkan detail dalam aktivitas yang sama (menambahkan fragmen ke FrameLayout), atau memulai aktivitas baru (tempat fragmen bisa ditampilkan).

Kotlin

class TitlesFragment : ListFragment() {

    private var dualPane: Boolean = false
    private var curCheckPosition = 0

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)

        // Populate list with our static array of titles.
        listAdapter = ArrayAdapter<String>(
                activity,
                android.R.layout.simple_list_item_activated_1,
                Shakespeare.TITLES
        )

        // Check to see if we have a frame in which to embed the details
        // fragment directly in the containing UI.
        val detailsFrame: View? = activity?.findViewById(R.id.details)
        dualPane = detailsFrame?.visibility == View.VISIBLE

        curCheckPosition = savedInstanceState?.getInt("curChoice", 0) ?: 0

        if (dualPane) {
            // In dual-pane mode, the list view highlights the selected item.
            listView.choiceMode = ListView.CHOICE_MODE_SINGLE
            // Make sure our UI is in the correct state.
            showDetails(curCheckPosition)
        }
    }

    override fun onSaveInstanceState(outState: Bundle) {
        super.onSaveInstanceState(outState)
        outState.putInt("curChoice", curCheckPosition)
    }

    override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) {
        showDetails(position)
    }

    /**
     * Helper function to show the details of a selected item, either by
     * displaying a fragment in-place in the current UI, or starting a
     * whole new activity in which it is displayed.
     */
    private fun showDetails(index: Int) {
        curCheckPosition = index

        if (dualPane) {
            // We can display everything in-place with fragments, so update
            // the list to highlight the selected item and show the data.
            listView.setItemChecked(index, true)

            // Check what fragment is currently shown, replace if needed.
            var details = fragmentManager?.findFragmentById(R.id.details) as? DetailsFragment
            if (details?.shownIndex != index) {
                // Make new fragment to show this selection.
                details = DetailsFragment.newInstance(index)

                // Execute a transaction, replacing any existing fragment
                // with this one inside the frame.
                fragmentManager?.beginTransaction()?.apply {
                    if (index == 0) {
                        replace(R.id.details, details)
                    } else {
                        replace(R.id.a_item, details)
                    }
                    setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
                    commit()
                }
            }

        } else {
            // Otherwise we need to launch a new activity to display
            // the dialog fragment with selected text.
            val intent = Intent().apply {
                setClass(activity, DetailsActivity::class.java)
                putExtra("index", index)
            }
            startActivity(intent)
        }
    }
}

Java

public static class TitlesFragment extends ListFragment {
    boolean dualPane;
    int curCheckPosition = 0;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        // Populate list with our static array of titles.
        setListAdapter(new ArrayAdapter<String>(getActivity(),
                android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));

        // Check to see if we have a frame in which to embed the details
        // fragment directly in the containing UI.
        View detailsFrame = getActivity().findViewById(R.id.details);
        dualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;

        if (savedInstanceState != null) {
            // Restore last state for checked position.
            curCheckPosition = savedInstanceState.getInt("curChoice", 0);
        }

        if (dualPane) {
            // In dual-pane mode, the list view highlights the selected item.
            getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
            // Make sure our UI is in the correct state.
            showDetails(curCheckPosition);
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putInt("curChoice", curCheckPosition);
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        showDetails(position);
    }

    /**
     * Helper function to show the details of a selected item, either by
     * displaying a fragment in-place in the current UI, or starting a
     * whole new activity in which it is displayed.
     */
    void showDetails(int index) {
        curCheckPosition = index;

        if (dualPane) {
            // We can display everything in-place with fragments, so update
            // the list to highlight the selected item and show the data.
            getListView().setItemChecked(index, true);

            // Check what fragment is currently shown, replace if needed.
            DetailsFragment details = (DetailsFragment)
                    getSupportFragmentManager().findFragmentById(R.id.details);
            if (details == null || details.getShownIndex() != index) {
                // Make new fragment to show this selection.
                details = DetailsFragment.newInstance(index);

                // Execute a transaction, replacing any existing fragment
                // with this one inside the frame.
                FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
                if (index == 0) {
                    ft.replace(R.id.details, details);
                } else {
                    ft.replace(R.id.a_item, details);
                }
                ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
                ft.commit();
            }

        } else {
            // Otherwise we need to launch a new activity to display
            // the dialog fragment with selected text.
            Intent intent = new Intent();
            intent.setClass(getActivity(), DetailsActivity.class);
            intent.putExtra("index", index);
            startActivity(intent);
        }
    }
}

Fragmen kedua, DetailsFragment menampilkan rangkuman pemutaran untuk item yang dipilih dari daftar dari TitlesFragment:

Kotlin

    class DetailsFragment : Fragment() {

        val shownIndex: Int by lazy {
            arguments?.getInt("index", 0) ?: 0
        }

        override fun onCreateView(
                inflater: LayoutInflater,
                container: ViewGroup?,
                savedInstanceState: Bundle?
        ): View? {
            if (container == null) {
                // We have different layouts, and in one of them this
                // fragment's containing frame doesn't exist. The fragment
                // may still be created from its saved state, but there is
                // no reason to try to create its view hierarchy because it
                // isn't displayed. Note this isn't needed -- we could just
                // run the code below, where we would create and return the
                // view hierarchy; it would just never be used.
                return null
            }

            val text = TextView(activity).apply {
                val padding: Int = TypedValue.applyDimension(
                        TypedValue.COMPLEX_UNIT_DIP,
                        4f,
                        activity?.resources?.displayMetrics
                ).toInt()
                setPadding(padding, padding, padding, padding)
                text = Shakespeare.DIALOGUE[shownIndex]
            }
            return ScrollView(activity).apply {
                addView(text)
            }
        }

        companion object {
            /**
             * Create a new instance of DetailsFragment, initialized to
             * show the text at 'index'.
             */
            fun newInstance(index: Int): DetailsFragment {
                val f = DetailsFragment()

                // Supply index input as an argument.
                val args = Bundle()
                args.putInt("index", index)
                f.arguments = args

                return f
            }
        }
    }
}

Java

public static class DetailsFragment extends Fragment {
    /**
     * Create a new instance of DetailsFragment, initialized to
     * show the text at 'index'.
     */
    public static DetailsFragment newInstance(int index) {
        DetailsFragment f = new DetailsFragment();

        // Supply index input as an argument.
        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);

        return f;
    }

    public int getShownIndex() {
        return getArguments().getInt("index", 0);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        if (container == null) {
            // We have different layouts, and in one of them this
            // fragment's containing frame doesn't exist. The fragment
            // may still be created from its saved state, but there is
            // no reason to try to create its view hierarchy because it
            // isn't displayed. Note this isn't needed -- we could just
            // run the code below, where we would create and return the
            // view hierarchy; it would just never be used.
            return null;
        }

        ScrollView scroller = new ScrollView(getActivity());
        TextView text = new TextView(getActivity());
        int padding = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                4, getActivity().getResources().getDisplayMetrics());
        text.setPadding(padding, padding, padding, padding);
        scroller.addView(text);
        text.setText(Shakespeare.DIALOGUE[getShownIndex()]);
        return scroller;
    }
}

Ingatlah dari class TitlesFragment, bahwa, jika pengguna mengklik item daftar dan layout saat ini tidak menyertakan tampilan R.id.details (yaitu tempat DetailsFragment berada), maka aplikasi akan memulai aktivitas DetailsActivity untuk menampilkan materi item.

Inilah DetailsActivity, yang hanya menyematkan DetailsFragment untuk menampilkan rangkuman pemutaran yang dipilih saat layar dalam orientasi potret:

Kotlin

class DetailsActivity : FragmentActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish()
            return
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            val details = DetailsFragment().apply {
                arguments = intent.extras
            }
            supportFragmentManager.beginTransaction()
                    .add(android.R.id.content, details)
                    .commit()
        }
    }
}

Java

public static class DetailsActivity extends FragmentActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (getResources().getConfiguration().orientation
                == Configuration.ORIENTATION_LANDSCAPE) {
            // If the screen is now in landscape mode, we can show the
            // dialog in-line with the list so we don't need this activity.
            finish();
            return;
        }

        if (savedInstanceState == null) {
            // During initial setup, plug in the details fragment.
            DetailsFragment details = new DetailsFragment();
            details.setArguments(getIntent().getExtras());
            getSupportFragmentManager().beginTransaction().add(android.R.id.content, details).commit();
        }
    }
}

Perhatikan bahwa aktivitas ini menyelesaikan dirinya sendiri jika konfigurasinya lanskap, sehingga aktivitas utama dapat mengambil alih dan menampilkan DetailsFragment bersama TitlesFragment. Hal ini bisa terjadi jika pengguna memulai DetailsActivity saat dalam orientasi potret, tetapi berputar menjadi lanskap (yang memulai ulang aktivitas saat ini).

Sumber daya tambahan

Fragment digunakan dalam aplikasi demo Sunflower.