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:
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.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.
Tambahkan tujuan aktivitas - Mengganti panggilan
startActivity()
dengan tindakan menggunakan tujuan aktivitas.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:
- Mengupdate komponen UI dengan NavigationUI - Pelajari cara mengelola navigasi dengan panel aplikasi atas, panel navigasi, dan navigasi bawah
- Menguji Navigasi - Pelajari cara menguji alur kerja navigasi untuk aplikasi Anda