Melakukan migrasi ke komponen Navigasi

Komponen Navigasi adalah library yang dapat mengelola navigasi kompleks, animasi transisi, deep linking, dan argumen pemeriksaan waktu kompilasi yang melewati layar di aplikasi Anda.

Dokumen ini berfungsi sebagai panduan umum tentang memigrasikan aplikasi yang ada untuk menggunakan komponen Navigasi.

Pada level tinggi, migrasi melibatkan langkah-langkah berikut:

  1. Pindahkan logika UI khusus layar dari aktivitas - Memindahkan logika UI aplikasi Anda dari aktivitas, untuk memastikan bahwa setiap aktivitas hanya memiliki logika komponen UI navigasi global, seperti Toolbar, saat mendelegasikan penerapan setiap layar ke fragmen atau tujuan khusus.

  2. Integrasikan komponen Navigasi - Untuk setiap aktivitas, buat grafik navigasi yang berisi satu atau beberapa fragmen yang dikelola oleh aktivitas tersebut. Ganti transaksi fragmen dengan operasi komponen Navigasi.

  3. Tambahkan tujuan aktivitas - Mengganti panggilan startActivity() dengan tindakan menggunakan tujuan aktivitas.

  4. Gabungkan aktivitas - Menggabungkan grafik navigasi jika beberapa aktivitas memiliki tata letak yang sama.

Prasyarat

Dengan menggunakan panduan ini, Anda diasumsikan telah memigrasikan aplikasi untuk menggunakan library AndroidX. Jika Anda belum melakukannya, migrasikan project untuk menggunakan AndroidX sebelum melanjutkan.

Memindahkan logika UI khusus layar dari aktivitas

Aktivitas adalah komponen level sistem yang memudahkan interaksi grafis antara aplikasi dan Android. Aktivitas terdaftar dalam manifes aplikasi Anda, sehingga Android mengetahui aktivitas mana yang tersedia untuk diluncurkan. Class aktivitas juga memungkinkan aplikasi Anda bereaksi terhadap perubahan Android, seperti saat UI aplikasi masuk atau keluar dari latar depan, berputar, dan sebagainya. Aktivitas ini juga dapat berfungsi sebagai tempat untuk berbagi status antar layar.

Dalam konteks aplikasi, aktivitas harus berfungsi sebagai host untuk navigasi serta harus berisi logika dan pengetahuan tentang cara melakukan transisi di antara layar, meneruskan data, dan sebagainya. Namun, pengelolaan detail UI sebaiknya dilakukan di bagian UI yang lebih kecil dan dapat digunakan kembali. Penerapan yang direkomendasikan untuk pola ini adalah fragmen. Lihat Aktivitas Tunggal: Mengapa, Kapan, dan Bagaimana untuk mempelajari keuntungan menggunakan fragmen lebih lanjut. Navigasi mendukung fragmen melalui dependensi fragmen navigasi. Navigasi juga mendukung jenis tujuan khusus.

Jika aplikasi Anda tidak menggunakan fragmen, hal pertama yang harus dilakukan adalah memigrasikan setiap layar di aplikasi untuk menggunakan fragmen. Anda tidak akan menghapus aktivitas pada tahap ini. Namun, Anda akan membuat fragmen untuk menunjukkan layar dan memisahkan logika UI berdasarkan tanggung jawab.

Menggunakan fragmen

Untuk menggambarkan proses menggunakan fragmen, mari kita mulai dengan contoh aplikasi yang terdiri dari dua layar: layar listingan produk dan layar detail produk. Dengan mengklik produk di layar listingan, pengguna akan diarahkan ke layar detail untuk mempelajari produk tersebut lebih lanjut.

Dalam contoh ini, layar detail dan listingan saat ini merupakan aktivitas terpisah.

Membuat Tata Letak Baru untuk Menghosting UI

Untuk menggunakan fragmen, mulai dengan membuat file tata letak baru agar aktivitas dapat menghosting fragmen. Tata letak ini akan menggantikan tata letak tampilan konten aktivitas saat ini.

Untuk tampilan sederhana, Anda dapat menggunakan FrameLayout, seperti yang ditunjukkan dalam contoh berikut product_list_host:

<FrameLayout
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:id="@+id/main_content"
   android:layout_height="match_parent"
   android:layout_width="match_parent" />

Atribut id merujuk pada bagian konten yang akan ditambahkan fragmen nanti.

Selanjutnya, dalam fungsi onCreate() aktivitas Anda, ubah referensi file tata letak di fungsi onCreate aktivitas untuk mengarahkan ke file tata letak baru ini:

Kotlin

class ProductListActivity : AppCompatActivity() {
    ...
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        // Replace setContentView(R.layout.product_list) with the line below
        setContentView(R.layout.product_list_host)
        ...
    }
}

Java

public class ProductListActivity extends AppCompatActivity {
    ...
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        ...
        // Replace setContentView(R.layout.product_list); with the line below
        setContentView(R.layout.product_list_host);
        ...
    }
}

Tata letak yang ada (dalam contoh ini, product_list) digunakan sebagai tampilan root untuk fragmen yang akan dibuat.

Membuat fragmen

Buat fragmen baru untuk mengelola UI layar Anda. Sebaiknya tetaplah konsisten dengan hostname aktivitas Anda. Cuplikan di bawah ini menggunakan ProductListFragment, misalnya:

Kotlin

class ProductListFragment : Fragment() {
    // Leave empty for now.
}

Java

public class ProductListFragment extends Fragment {
    // Leave empty for now.
}

Memindahkan logika aktivitas ke fragmen

Setelah menentukan definisi fragmen, langkah berikutnya adalah memindahkan logika UI untuk layar ini dari aktivitas ke fragmen baru ini. Jika Anda berasal dari arsitektur berbasis aktivitas, mungkin ada banyak logika pembuatan tampilan yang berlangsung dalam fungsi onCreate() aktivitas Anda.

Berikut adalah contoh layar berbasis aktivitas dengan logika UI yang perlu dipindahkan:

Kotlin

class ProductListActivity : AppCompatActivity() {

    // Views and/or ViewDataBinding references, Adapters...
    private lateinit var productAdapter: ProductAdapter
    private lateinit var binding: ProductListActivityBinding

    ...

    // ViewModels, System Services, other Dependencies...
    private val viewModel: ProductListViewModel by viewModels()

    ...

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

        // View initialization logic
        DataBindingUtil.setContentView(this, R.layout.product_list_activity)

        // Post view initialization logic
        // Connect adapters
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener {...}

        // Subscribe to state
        viewModel.products.observe(this, Observer { myProducts ->
            ...
        })

        // ...and so on
    }
   ...
}

Java

public class ProductListActivity extends AppCompatActivity {

    // Views and/or ViewDataBinding references, adapters...
    private ProductAdapter productAdapter;
    private ProductListActivityBinding binding;

    ...

    // ViewModels, system services, other dependencies...
    private ProductListViewModel viewModel;

    ...

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

        // View initialization logic
        DataBindingUtil.setContentView(this, R.layout.product_list_activity);

        // Post view initialization logic
        // Connect adapters
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);

        // Initialize ViewModels and other dependencies
        ProductListViewModel viewModel = new ViewModelProvider(this).get(ProductListViewModel.java);

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener(v -> { ... });

        // Subscribe to state
        viewModel.getProducts().observe(this, myProducts ->
            ...
       );

       // ...and so on
   }

Aktivitas Anda mungkin juga mengontrol waktu dan cara pengguna menavigasi ke layar berikutnya, seperti yang ditunjukkan dalam contoh berikut:

Kotlin

    // Provided to ProductAdapter in ProductListActivity snippet.
    private val productClickCallback = ProductClickCallback { product ->
        show(product)
    }

    fun show(product: Product) {
        val intent = Intent(this, ProductActivity::class.java)
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id)
        startActivity(intent)
    }

Java

// Provided to ProductAdapter in ProductListActivity snippet.
private ProductClickCallback productClickCallback = this::show;

private void show(Product product) {
    Intent intent = new Intent(this, ProductActivity.class);
    intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId());
    startActivity(intent);
}

Di dalam fragmen, Anda mendistribusikan tugas ini antara onCreateView() dan onViewCreated(), hanya dengan logika navigasi yang tersisa dalam aktivitas:

Kotlin

class ProductListFragment : Fragment() {

    private lateinit var binding: ProductListFragmentBinding
    private val viewModel: ProductListViewModel by viewModels()

     // View initialization logic
    override fun onCreateView(inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?): View? {
        binding = DataBindingUtil.inflate(
                inflater,
                R.layout.product_list,
                container,
                false
        )
        return binding.root
    }

    // Post view initialization logic
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // Connect adapters
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener {...}

        // Subscribe to state
        viewModel.products.observe(this, Observer { myProducts ->
            ...
        })

        // ...and so on
    }

    // Provided to ProductAdapter
    private val productClickCallback = ProductClickCallback { product ->
        if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
            (requireActivity() as ProductListActivity).show(product)
        }
    }
    ...
}

Java

public class ProductListFragment extends Fragment {

    private ProductAdapter productAdapter;
    private ProductListFragmentBinding binding;

    // View initialization logic
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater,
            @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        binding = DataBindingUtil.inflate(
                inflater,
                R.layout.product_list_fragment,
                container,
                false);
        return binding.getRoot();
    }

    // Post view initialization logic
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {

        // Connect adapters
        binding.productsList.setAdapter(productAdapter);

        // Initialize ViewModels and other dependencies
        ProductListViewModel viewModel = new ViewModelProvider(this)
                .get(ProductListViewModel.class);

        // Initialize view properties, set click listeners, etc.
        binding.productsSearchBtn.setOnClickListener(...)

        // Subscribe to state
        viewModel.getProducts().observe(this, myProducts -> {
            ...
       });

       // ...and so on

    // Provided to ProductAdapter
    private ProductClickCallback productClickCallback = new ProductClickCallback() {
        @Override
        public void onClick(Product product) {
            if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) {
                ((ProductListActivity) requireActivity()).show(product);
            }
        }
    };
    ...
}

Di ProductListFragment, perhatikan bahwa tidak ada panggilan ke setContentView() untuk memperluas dan menghubungkan tata letak. Dalam fragmen, onCreateView() menginisialisasi tampilan root. onCreateView() mengambil instance LayoutInflater yang dapat digunakan untuk memperluas tampilan root berdasarkan file resource tata letak. Contoh ini menggunakan kembali tata letak product_list yang ada yang digunakan oleh aktivitas karena tata letak tersebut tidak perlu diubah.

Jika ada logika UI di fungsi onStart(), onResume(), onPause(), atau onStop() aktivitas Anda yang tidak terkait dengan navigasi, Anda dapat memindahkannya ke fungsi yang sesuai dengan nama yang sama pada fragmen.

Menginisialisasi fragmen dalam aktivitas host

Setelah memindahkan semua logika UI ke fragmen, hanya logika navigasi yang harus tetap berada dalam aktivitas.

Kotlin

class ProductListActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.product_list_host)
    }

    fun show(product: Product) {
        val intent = Intent(this, ProductActivity::class.java)
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.id)
        startActivity(intent)
    }
}

Java

public class ProductListActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.product_list_host);
    }

    public void show(Product product) {
        Intent intent = new Intent(this, ProductActivity.class);
        intent.putExtra(ProductActivity.KEY_PRODUCT_ID, product.getId());
        startActivity(intent);
    }
}

Langkah terakhir adalah membuat instance fragmen di onCreate(), segera setelah menyetel tampilan konten:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.product_list_host)

    if (savedInstanceState == null) {
        val fragment = ProductListFragment()
        supportFragmentManager
                .beginTransaction()
                .add(R.id.main_content, fragment)
                .commit()
    }
}

Java

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.product_list_host);

    if (savedInstanceState == null) {
        ProductListFragment fragment = new ProductListFragment();
        getSupportFragmentManager()
                .beginTransaction()
                .add(R.id.main_content, fragment)
                .commit();
    }
}

Seperti yang ditunjukkan dalam contoh ini, FragmentManager secara otomatis menyimpan dan memulihkan fragmen melalui perubahan konfigurasi, sehingga Anda hanya perlu menambahkan fragmen jika savedInstanceState bernilai null.

Meneruskan intent tambahan ke fragmen

Jika aktivitas menerima Extras melalui intent, Anda dapat meneruskannya ke fragmen secara langsung sebagai argumen.

Dalam contoh ini, ProductDetailsFragment menerima argumennya langsung dari intent tambahan aktivitas:

Kotlin

...

if (savedInstanceState == null) {
    val fragment = ProductDetailsFragment()

    // Intent extras and Fragment Args are both of type android.os.Bundle.
    fragment.arguments = intent.extras

    supportFragmentManager
            .beginTransaction()
            .add(R.id.main_content, fragment)
            .commit()
}

...

Java

...

if (savedInstanceState == null) {
    ProductDetailsFragment fragment = new ProductDetailsFragment();

    // Intent extras and fragment Args are both of type android.os.Bundle.
    fragment.setArguments(getIntent().getExtras());

    getSupportFragmentManager()
            .beginTransaction()
            .add(R.id.main_content, fragment)
            .commit();
}

...

Pada tahap ini, Anda dapat melakukan pengujian saat menjalankan aplikasi dengan mengupdate layar pertama untuk menggunakan fragmen. Lanjutkan memigrasi sisa layar berbasis aktivitas Anda, yang memerlukan waktu untuk diuji setiap setelah iterasi.

Mengintegrasikan komponen Navigasi

Setelah menggunakan arsitektur berbasis fragmen, Anda siap untuk mulai mengintegrasikan komponen Navigasi.

Pertama, tambahkan dependensi Navigasi terbaru ke project Anda, dengan mengikuti petunjuk di Catatan rilis library Navigasi.

Membuat grafik navigasi

Komponen Navigasi merepresentasikan konfigurasi navigasi aplikasi Anda dalam file resource sebagai grafik, seperti tampilan aplikasi Anda. Hal ini membantu menjaga navigasi aplikasi tetap teratur di luar codebase dan memberi Anda cara untuk mengedit navigasi aplikasi secara visual.

Untuk membuat grafik navigasi, mulai dengan membuat folder resource baru bernama navigation. Untuk menambahkan grafik, klik kanan direktori ini, lalu pilih New > Navigation resource file.

Komponen Navigasi menggunakan aktivitas sebagai host untuk navigasi dan menukar fragmen tertentu ke host tersebut saat pengguna melakukan navigasi dalam aplikasi Anda. Sebelum dapat mulai mengatur tata letak navigasi aplikasi secara visual, Anda harus mengonfigurasi NavHost di dalam aktivitas yang akan menghosting grafik ini. Karena menggunakan fragmen, kita dapat menggunakan penerapan NavHost default, NavHostFragment, dari komponen Navigasi.

NavHostFragment dikonfigurasi melalui FragmentContainerView yang ditempatkan di dalam aktivitas host, seperti yang ditunjukkan dalam contoh berikut:

<androidx.fragment.app.FragmentContainerView
   android:name="androidx.navigation.fragment.NavHostFragment"
   app:navGraph="@navigation/product_list_graph"
   app:defaultNavHost="true"
   android:id="@+id/main_content"
   android:layout_width="match_parent"
   android:layout_height="match_parent" />

Atribut app:NavGraph mengarah ke grafik navigasi yang terkait dengan host navigasi ini. Menyetel properti ini akan memperluas grafik navigasi dan menetapkan properti grafik pada NavHostFragment. Atribut app:defaultNavHost memastikan bahwa NavHostFragment Anda mengintersep tombol Kembali sistem.

Jika Anda menggunakan navigasi level atas seperti DrawerLayout atau BottomNavigationView, FragmentContainerView akan menggantikan elemen tampilan konten utama Anda. Lihat Mengupdate komponen UI dengan NavigationUI untuk mengetahui contohnya.

Untuk tata letak sederhana, Anda dapat menyertakan elemen FragmentContainerView ini sebagai turunan ViewGroup root:

<FrameLayout
   xmlns:app="http://schemas.android.com/apk/res-auto"
   xmlns:android="http://schemas.android.com/apk/res/android"
   android:layout_height="match_parent"
   android:layout_width="match_parent">

<androidx.fragment.app.FragmentContainerView
   android:id="@+id/main_content"
   android:name="androidx.navigation.fragment.NavHostFragment"
   app:navGraph="@navigation/product_list_graph"
   app:defaultNavHost="true"
   android:layout_width="match_parent"
   android:layout_height="match_parent" />

</FrameLayout>

Jika mengklik tab Design di bagian bawah, Anda akan melihat grafik yang serupa dengan yang ditampilkan di bawah. Di sisi kiri atas grafik, pada bagian Destinations, Anda dapat melihat referensi untuk aktivitas NavHost dalam bentuk layout_name (resource_id).

Klik tombol plus di dekat bagian atas untuk menambahkan fragmen ke grafik ini.

Komponen Navigasi merujuk pada masing-masing layar sebagai tujuan. Tujuan dapat berupa fragmen, aktivitas, atau tujuan kustom. Anda dapat menambahkan jenis tujuan apa pun ke grafik. Namun, perhatikan bahwa tujuan aktivitas dianggap sebagai tujuan terminal, karena setelah menavigasi ke tujuan aktivitas, Anda akan beroperasi dalam host dan grafik navigasi terpisah.

Komponen Navigasi merujuk pada cara pengguna dapat berpindah dari satu tujuan ke tujuan lain sebagai tindakan. Tindakan juga dapat menggambarkan animasi transisi dan perilaku muncul.

Menghapus transaksi fragmen

Setelah dapat menggunakan komponen Navigasi, dan jika melakukan navigasi antara layar berbasis fragmen pada aktivitas yang sama, Anda dapat menghapus interaksi FragmentManager.

Jika aplikasi Anda menggunakan beberapa fragmen pada aktivitas yang sama atau navigasi level atas seperti tata letak panel samping atau navigasi bawah, Anda mungkin menggunakan FragmentManager dan FragmentTransactions untuk menambahkan atau menggantikan fragmen di bagian konten utama UI. Fragmen ini sekarang dapat diganti dan disederhanakan menggunakan komponen Navigasi dengan memberikan tindakan untuk menautkan tujuan dalam grafik, lalu bernavigasi menggunakan NavController.

Berikut adalah beberapa skenario yang mungkin Anda temui beserta cara menggunakan migrasi untuk setiap skenario.

Satu aktivitas yang mengelola beberapa fragmen

Jika Anda memiliki satu aktivitas yang mengelola beberapa fragmen, kode aktivitas Anda dapat terlihat seperti ini:

Kotlin

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // Logic to load the starting destination
        //  when the Activity is first created
        if (savedInstanceState == null) {
            val fragment = ProductListFragment()
            supportFragmentManager.beginTransaction()
                    .add(R.id.fragment_container, fragment, ProductListFragment.TAG)
                    .commit()
        }
    }

    // Logic to navigate the user to another destination.
    // This may include logic to initialize and set arguments on the destination
    // fragment or even transition animations between the fragments (not shown here).
    fun navigateToProductDetail(productId: String) {
        val fragment = new ProductDetailsFragment()
        val args = Bundle().apply {
            putInt(KEY_PRODUCT_ID, productId)
        }
        fragment.arguments = args

        supportFragmentManager.beginTransaction()
                .addToBackStack(ProductDetailsFragment.TAG)
                .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG)
                .commit()
    }
}

Java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Logic to load the starting destination when the activity is first created.
        if (savedInstanceState == null) {
            val fragment = ProductListFragment()
            supportFragmentManager.beginTransaction()
                    .add(R.id.fragment_container, fragment, ProductListFragment.TAG)
                    .commit();
        }
    }

    // Logic to navigate the user to another destination.
    // This may include logic to initialize and set arguments on the destination
    // fragment or even transition animations between the fragments (not shown here).
    public void navigateToProductDetail(String productId) {
        Fragment fragment = new ProductDetailsFragment();
        Bundle args = new Bundle();
        args.putInt(KEY_PRODUCT_ID, productId);
        fragment.setArguments(args);

        getSupportFragmentManager().beginTransaction()
                .addToBackStack(ProductDetailsFragment.TAG)
                .replace(R.id.fragment_container, fragment, ProductDetailsFragment.TAG)
                .commit();
    }
}

Di dalam tujuan sumber, Anda mungkin perlu memanggil fungsi navigasi untuk merespons beberapa peristiwa, seperti yang ditunjukkan di bawah:

Kotlin

class ProductListFragment : Fragment() {
    ...
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // In this example a callback is passed to respond to an item clicked
        //  in a RecyclerView
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private val productClickCallback = ProductClickCallback { product ->
            (requireActivity() as MainActivity).navigateToProductDetail(product.id)
    }
}

Java

public class ProductListFragment extends Fragment  {
    ...
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
    // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private ProductClickCallback productClickCallback = product -> (
        ((MainActivity) requireActivity()).navigateToProductDetail(product.getId())
    );
}

Fungsi ini dapat diganti dengan mengupdate grafik navigasi Anda guna menetapkan tujuan awal dan tindakan untuk menautkan tujuan dan menentukan argumen jika diperlukan:

<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_detail" />
    </fragment>
    <fragment
        android:id="@+id/product_detail"
        android:name="com.example.android.persistence.ui.ProductDetailFragment"
        android:label="Product Detail"
        tools:layout="@layout/product_detail">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>
</navigation>

Kemudian, Anda dapat mengupdate aktivitas:

Kotlin

class MainActivity : AppCompatActivity() {

    // No need to load the start destination, handled automatically by the Navigation component
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    // No need to load the start destination, handled automatically by the Navigation component
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
}

Aktivitas ini tidak memerlukan metode navigateToProductDetail() lagi. Pada bagian berikutnya, kita akan mengupdate ProductListFragment untuk menggunakan NavController agar dapat menavigasi ke layar detail produk berikutnya.

Meneruskan argumen dengan aman

Komponen Navigasi memiliki plugin Gradle yang disebut Safe Args yang membuat class objek dan builder sederhana untuk akses yang aman ke argumen yang ditentukan bagi tujuan dan tindakan.

Setelah plugin diterapkan, setiap argumen yang ditentukan pada tujuan dalam grafik navigasi Anda akan membuat framework komponen Navigasi menghasilkan class Arguments yang memberikan argumen yang aman ke tujuan target. Menentukan tindakan akan membuat plugin menghasilkan class konfigurasi Directions yang dapat digunakan untuk memberi tahu NavController cara menavigasi pengguna ke tujuan target. Saat tindakan mengarah ke tujuan yang memerlukan argumen, class Directions yang dihasilkan menyertakan metode konstruktor yang memerlukan parameter tersebut.

Di dalam fragmen, gunakan NavController dan class Directions yang dihasilkan untuk memberikan argumen type-safe ke tujuan target, seperti yang ditunjukkan dalam contoh berikut:

Kotlin

class ProductListFragment : Fragment() {

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = ProductAdapter(productClickCallback)
        binding.productsList.setAdapter(productAdapter)
    }
    ...

    // The callback makes the call to the NavController to make the transition.
    private val productClickCallback = ProductClickCallback { product ->
        val directions = ProductListDirections.navigateToProductDetail(product.id)
        findNavController().navigate(directions)
    }
}

Java

public class ProductListFragment extends Fragment  {
    ...
    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
        // In this example a callback is passed to respond to an item clicked in a RecyclerView
        productAdapter = new ProductAdapter(productClickCallback);
        binding.productsList.setAdapter(productAdapter);
    }
    ...

    // The callback makes the call to the activity to make the transition.
    private ProductClickCallback productClickCallback = product -> {
        ProductListDirections.ViewProductDetails directions =
                ProductListDirections.navigateToProductDetail(product.getId());
        NavHostFragment.findNavController(this).navigate(directions);
    };
}

Navigasi Level Atas

Jika aplikasi Anda menggunakan DrawerLayout, mungkin ada banyak logika konfigurasi dalam aktivitas Anda yang mengelola pembukaan dan penutupan panel samping serta menavigasi ke tujuan lain.

Aktivitas yang dihasilkan dapat terlihat seperti ini:

Kotlin

class MainActivity : AppCompatActivity(),
    NavigationView.OnNavigationItemSelectedListener {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val toolbar: Toolbar = findViewById(R.id.toolbar)
        setSupportActionBar(toolbar)

        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        val navView: NavigationView = findViewById(R.id.nav_view)
        val toggle = ActionBarDrawerToggle(
                this,
                drawerLayout,
                toolbar,
                R.string.navigation_drawer_open, 
                R.string.navigation_drawer_close
        )
        drawerLayout.addDrawerListener(toggle)
        toggle.syncState()

        navView.setNavigationItemSelectedListener(this)
    }

    override fun onBackPressed() {
        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        if (drawerLayout.isDrawerOpen(GravityCompat.START)) {
            drawerLayout.closeDrawer(GravityCompat.START)
        } else {
            super.onBackPressed()
        }
    }

    override fun onNavigationItemSelected(item: MenuItem): Boolean {
        // Handle navigation view item clicks here.
        when (item.itemId) {
            R.id.home -> {
                val homeFragment = HomeFragment()
                show(homeFragment)
            }
            R.id.gallery -> {
                val galleryFragment = GalleryFragment()
                show(galleryFragment)
            }
            R.id.slide_show -> {
                val slideShowFragment = SlideShowFragment()
                show(slideShowFragment)
            }
            R.id.tools -> {
                val toolsFragment = ToolsFragment()
                show(toolsFragment)
            }
        }
        val drawerLayout: DrawerLayout = findViewById(R.id.drawer_layout)
        drawerLayout.closeDrawer(GravityCompat.START)
        return true
    }
}

private fun show(fragment: Fragment) {

    val drawerLayout = drawer_layout as DrawerLayout
    val fragmentManager = supportFragmentManager

    fragmentManager
            .beginTransaction()
            .replace(R.id.main_content, fragment)
            .commit()

    drawerLayout.closeDrawer(GravityCompat.START)
}

Java

public class MainActivity extends AppCompatActivity
        implements NavigationView.OnNavigationItemSelectedListener {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        NavigationView navigationView = findViewById(R.id.nav_view);
        ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
                this,
                drawer,
                toolbar,
                R.string.navigation_drawer_open,
                R.string.navigation_drawer_close);
        drawer.addDrawerListener(toggle);
        toggle.syncState();

        navigationView.setNavigationItemSelectedListener(this);
    }

    @Override
    public void onBackPressed() {
        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        if (drawer.isDrawerOpen(GravityCompat.START)) {
            drawer.closeDrawer(GravityCompat.START);
        } else {
            super.onBackPressed();
        }
    }

    @Override
    public boolean onNavigationItemSelected(MenuItem item) {
        // Handle navigation view item clicks here.
        int id = item.getItemId();

        if (id == R.id.home) {
            Fragment homeFragment = new HomeFragment();
            show(homeFragment);
        } else if (id == R.id.gallery) {
            Fragment galleryFragment = new GalleryFragment();
            show(galleryFragment);
        } else if (id == R.id.slide_show) {
            Fragment slideShowFragment = new SlideShowFragment();
            show(slideShowFragment);
        } else if (id == R.id.tools) {
            Fragment toolsFragment = new ToolsFragment();
            show(toolsFragment);
        }

        DrawerLayout drawer = findViewById(R.id.drawer_layout);
        drawer.closeDrawer(GravityCompat.START);
        return true;
    }

    private void show(Fragment fragment) {

        DrawerLayout drawerLayout = findViewById(R.id.drawer_layout);
        FragmentManager fragmentManager = getSupportFragmentManager();

        fragmentManager
                .beginTransaction()
                .replace(R.id.main_content, fragment)
                .commit();

        drawerLayout.closeDrawer(GravityCompat.START);
    }
}

Setelah menambahkan komponen Navigasi ke project Anda dan membuat grafik navigasi, tambahkan setiap tujuan konten dari grafik (seperti Home, Gallery, SlideShow, dan Tools dari contoh di atas). Pastikan bahwa nilai id item menu Anda cocok dengan nilai id tujuan terkait, seperti yang ditunjukkan di bawah:

<!-- activity_main_drawer.xml -->
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    tools:showIn="navigation_view">

    <group android:checkableBehavior="single">
        <item
            android:id="@+id/home"
            android:icon="@drawable/ic_menu_camera"
            android:title="@string/menu_home" />
        <item
            android:id="@+id/gallery"
            android:icon="@drawable/ic_menu_gallery"
            android:title="@string/menu_gallery" />
        <item
            android:id="@+id/slide_show"
            android:icon="@drawable/ic_menu_slideshow"
            android:title="@string/menu_slideshow" />
        <item
            android:id="@+id/tools"
            android:icon="@drawable/ic_menu_manage"
            android:title="@string/menu_tools" />
    </group>
</menu>
<!-- activity_main_graph.xml -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/main_graph"
    app:startDestination="@id/home">

    <fragment
        android:id="@+id/home"
        android:name="com.example.HomeFragment"
        android:label="Home"
        tools:layout="@layout/home" />

    <fragment
        android:id="@+id/gallery"
        android:name="com.example.GalleryFragment"
        android:label="Gallery"
        tools:layout="@layout/gallery" />

    <fragment
        android:id="@+id/slide_show"
        android:name="com.example.SlideShowFragment"
        android:label="Slide Show"
        tools:layout="@layout/slide_show" />

    <fragment
        android:id="@+id/tools"
        android:name="com.example.ToolsFragment"
        android:label="Tools"
        tools:layout="@layout/tools" />

</navigation>

Jika nilai id dari menu dan grafik cocok, Anda dapat menyambungkan NavController untuk aktivitas ini guna menangani navigasi secara otomatis berdasarkan item menu. NavController juga menangani pembukaan dan penutupan DrawerLayout serta menangani perilaku tombol Atas dan Kembali dengan tepat.

Kemudian, MainActivity dapat diupdate untuk menyambungkan NavController ke Toolbar dan NavigationView.

Untuk contohnya, lihat cuplikan berikut:

Kotlin

class MainActivity : AppCompatActivity()  {

    val drawerLayout by lazy { findViewById<DrawerLayout>(R.id.drawer_layout) }
    val navController by lazy {
      (supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment).navController
    }
    val navigationView by lazy { findViewById<NavigationView>(R.id.nav_view) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        setSupportActionBar(toolbar)

        // Show and Manage the Drawer and Back Icon
        setupActionBarWithNavController(navController, drawerLayout)

        // Handle Navigation item clicks
        // This works with no further action on your part if the menu and destination id’s match.
        navigationView.setupWithNavController(navController)

    }

    override fun onSupportNavigateUp(): Boolean {
        // Allows NavigationUI to support proper up navigation or the drawer layout
        // drawer menu, depending on the situation
        return navController.navigateUp(drawerLayout)
    }
}

Java

public class MainActivity extends AppCompatActivity {

    private DrawerLayout drawerLayout;
    private NavController navController;
    private NavigationView navigationView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        drawerLayout = findViewById(R.id.drawer_layout);
        NavHostFragment navHostFragment = (NavHostFragment)
            getSupportFragmentManager().findFragmentById(R.id.main_content);
        navController = navHostFragment.getNavController();
        navigationView = findViewById(R.id.nav_view);

        Toolbar toolbar = findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

        // Show and Manage the Drawer and Back Icon
        NavigationUI.setupActionBarWithNavController(this, navController, drawerLayout);

        // Handle Navigation item clicks
        // This works with no further action on your part if the menu and destination id’s match.
        NavigationUI.setupWithNavController(navigationView, navController);

    }

    @Override
    public boolean onSupportNavigateUp() {
        // Allows NavigationUI to support proper up navigation or the drawer layout
        // drawer menu, depending on the situation.
        return NavigationUI.navigateUp(navController, drawerLayout);

    }
}

Anda dapat menggunakan teknik yang sama dengan navigasi berbasis BottomNavigationView dan navigasi berbasis Menu. Lihat Mengupdate komponen UI dengan NavigationUI untuk mengetahui contoh lainnya.

Menambahkan tujuan aktivitas

Setelah setiap layar di aplikasi Anda disambungkan untuk menggunakan komponen Navigasi, dan Anda tidak menggunakan FragmentTransactions lagi untuk melakukan transisi antara tujuan berbasis fragmen, langkah berikutnya adalah menghilangkan panggilan startActivity.

Terlebih dahulu, identifikasi tempat di aplikasi Anda yang menyimpan dua grafik navigasi terpisah dan menggunakan startActivity untuk melakukan transisi di antaranya.

Contoh ini berisi dua grafik (A dan B) dan panggilan startActivity() untuk transisi dari A ke B.

Kotlin

fun navigateToProductDetails(productId: String) {
    val intent = Intent(this, ProductDetailsActivity::class.java)
    intent.putExtra(KEY_PRODUCT_ID, productId)
    startActivity(intent)
}

Java

private void navigateToProductDetails(String productId) {
    Intent intent = new Intent(this, ProductDetailsActivity.class);
    intent.putExtra(KEY_PRODUCT_ID, productId);
    startActivity(intent);

Selanjutnya, ganti dengan tujuan aktivitas di Grafik A yang menunjukkan navigasi ke aktivitas host Grafik B. Jika memiliki argumen untuk diteruskan ke tujuan awal Grafik B, Anda dapat menentukannya dalam definisi tujuan aktivitas.

Dalam contoh berikut, Grafik A menentukan tujuan aktivitas yang mengambil argumen product_id beserta tindakan. Grafik B tidak berisi perubahan.

Representasi XML dari Grafik A dan B dapat terlihat seperti ini:

<!-- Graph A -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List"
        tools:layout="@layout/product_list_fragment">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details_activity" />
    </fragment>

    <activity
        android:id="@+id/product_details_activity"
        android:name="com.example.android.persistence.ui.ProductDetailsActivity"
        android:label="Product Details"
        tools:layout="@layout/product_details_host">

        <argument
            android:name="product_id"
            app:argType="integer" />

    </activity>

</navigation>
<!-- Graph B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    app:startDestination="@id/product_details">

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details_fragment">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>

</navigation>

Anda dapat menavigasi ke aktivitas host Grafik B menggunakan mekanisme yang sama yang digunakan untuk menavigasi ke tujuan fragmen:

Kotlin

fun navigateToProductDetails(productId: String) {
    val directions = ProductListDirections.navigateToProductDetail(productId)
    findNavController().navigate(directions)
}

Java

private void navigateToProductDetails(String productId) {
    ProductListDirections.NavigateToProductDetail directions =
            ProductListDirections.navigateToProductDetail(productId);
    Navigation.findNavController(getView()).navigate(directions);

Meneruskan args tujuan aktivitas ke fragmen tujuan awal

Jika aktivitas tujuan menerima tambahan, seperti contoh sebelumnya, Anda dapat meneruskannya ke tujuan awal secara langsung sebagai argumen, tetapi Anda perlu menetapkan grafik navigasi host di dalam metode onCreate() aktivitas host secara manual sehingga intent tambahan dapat diteruskan sebagai argumen ke fragmen, seperti yang ditunjukkan di bawah:

Kotlin

class ProductDetailsActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.product_details_host)
        val navHostFragment = supportFragmentManager.findFragmentById(R.id.main_content) as NavHostFragment
        val navController = navHostFramgent.navController
        navController
                .setGraph(R.navigation.product_detail_graph, intent.extras)
    }

}

Java

public class ProductDetailsActivity extends AppCompatActivity {

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

        setContentView(R.layout.product_details_host);
        NavHostFragment navHostFragment = (NavHostFragment)
            getSupportFragmentManager().findFragmentById(R.id.main_content);
        NavController navController = navHostFragment.getNavController();
        navController
                .setGraph(R.navigation.product_detail_graph, getIntent().getExtras());
    }

}

Data dapat dikeluarkan dari argumen fragmen Bundle menggunakan class args yang dihasilkan, seperti yang ditunjukkan dalam contoh berikut:

Kotlin

class ProductDetailsFragment : Fragment() {

    val args by navArgs<ProductDetailsArgs>()

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        val productId = args.productId
        ...
    }
    ...

Java

public class ProductDetailsFragment extends Fragment {

    ProductDetailsArgs args;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        args = ProductDetailsArgs.fromBundle(requireArguments());
    }

    @Override
    public void onViewCreated(@NonNull View view,
            @Nullable Bundle savedInstanceState) {
       int productId = args.getProductId();
       ...
    }
    ...

Menggabungkan aktivitas

Anda dapat menggabungkan grafik navigasi jika beberapa aktivitas memiliki tata letak yang sama, seperti FrameLayout sederhana yang berisi satu fragmen. Di sebagian besar kasus ini, Anda dapat menggabungkan semua elemen dari setiap grafik navigasi dan mengupdate elemen tujuan aktivitas apa pun ke tujuan fragmen.

Contoh berikut menggabungkan Grafik A dan B dari bagian sebelumnya:

Sebelum menggabungkan:

<!-- Graph A -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List Fragment"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details_activity" />
    </fragment>
    <activity
        android:id="@+id/product_details_activity"
        android:name="com.example.android.persistence.ui.ProductDetailsActivity"
        android:label="Product Details Host"
        tools:layout="@layout/product_details_host">
        <argument android:name="product_id"
            app:argType="integer" />
    </activity>

</navigation>
<!-- Graph B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_detail_graph"
    app:startDestination="@id/product_details">

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>
</navigation>

Setelah menggabungkan:

<!-- Combined Graph A and B -->
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/product_list_graph"
    app:startDestination="@id/product_list">

    <fragment
        android:id="@+id/product_list"
        android:name="com.example.android.persistence.ui.ProductListFragment"
        android:label="Product List Fragment"
        tools:layout="@layout/product_list">
        <action
            android:id="@+id/navigate_to_product_detail"
            app:destination="@id/product_details" />
    </fragment>

    <fragment
        android:id="@+id/product_details"
        android:name="com.example.android.persistence.ui.ProductDetailsFragment"
        android:label="Product Details"
        tools:layout="@layout/product_details">
        <argument
            android:name="product_id"
            app:argType="integer" />
    </fragment>

</navigation>

Tetap menggunakan nama tindakan yang sama selama penggabungan dapat membuat proses ini berjalan lancar, sehingga tidak perlu mengubah codebase yang ada. Misalnya, navigateToProductDetail akan tetap sama di sini. Satu-satunya perbedaan adalah tindakan ini sekarang menunjukkan navigasi ke tujuan fragmen dalam NavHost yang sama, bukan tujuan aktivitas:

Kotlin

fun navigateToProductDetails(productId: String) {
    val directions = ProductListDirections.navigateToProductDetail(productId)
    findNavController().navigate(directions)
}

Java

private void navigateToProductDetails(String productId) {
    ProductListDirections.NavigateToProductDetail directions =
            ProductListDirections.navigateToProductDetail(productId);
    Navigation.findNavController(getView()).navigate(directions);

Referensi Lainnya

Untuk mengetahui informasi selengkapnya tentang navigasi, lihat topik berikut: