Việc chuyển đến một đích đến được thực hiện thông qua NavController
, một đối tượng quản lý hoạt động điều hướng ứng dụng trong một NavHost
. Mỗi NavHost
sẽ có NavController
tương ứng riêng. NavController
cung cấp vài cách khác nhau để điều hướng đến đích đến. Bạn có thể xem nội dung mô tả chi tiết ở phần bên dưới.
Để truy xuất NavController
cho một mảnh, hoạt động hoặc chế độ xem, sử dụng một trong các phương thức sau:
Kotlin:
Java:
NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, @IdRes int viewId)
Navigation.findNavController(View)
Sau khi truy xuất NavController
, bạn có thể gọi một trong các phương thức nạp chồng của navigate()
để điều hướng giữa các đích đến. Mỗi phương thức nạp chồng sẽ hỗ trợ cho nhiều tình huống di chuyển như mô tả trong các phần sau.
Dùng Safe Args để di chuyển dữ liệu đảm bảo an toàn về loại
Bạn nên di chuyển giữa các đích đến bằng trình bổ trợ Safe Args Gradle. Trình bổ trợ này tạo các lớp đơn giản cho đối tượng và trình tạo để điều hướng theo hình thức an toàn về loại giữa các đích đến. Bạn nên sử dụng Safe Args để điều hướng cũng như truyền dữ liệu giữa các đích đến.
Để thêm Safe Args vào dự án, hãy đưa classpath
sau vào tệp build.gradle
cấp cao nhất của bạn:
Groovy
buildscript { repositories { google() } dependencies { def nav_version = "2.5.3" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } }
Kotlin
buildscript { repositories { google() } dependencies { val nav_version = "2.5.3" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") } }
Bạn cũng phải áp dụng 1 trong 2 trình bổ trợ có sẵn.
Để tạo mã ngôn ngữ Java phù hợp với mô-đun Java và Kotlin, hãy thêm dòng này vào tệp build.gradle
của ứng dụng hoặc mô-đun:
Groovy
plugins { id 'androidx.navigation.safeargs' }
Kotlin
plugins { id("androidx.navigation.safeargs") }
Ngoài ra, để tạo mã Kotlin phù hợp với các mô-đun chỉ Kotlin, hãy thêm:
Groovy
plugins { id 'androidx.navigation.safeargs.kotlin' }
Kotlin
plugins { id("androidx.navigation.safeargs.kotlin") }
Bạn phải có android.useAndroidX=true
trong
tệp gradle.properties
theo hướng dẫn
Di chuyển sang AndroidX.
Sau khi bật trình bổ trợ Safe Args, mã do bạn tạo sẽ chứa các lớp và phương thức cho từng thao tác mà bạn xác định, cũng như các lớp tương ứng với từng đích đến của hoạt động gửi và nhận.
Safe Args tạo một lớp cho mỗi đích ban đầu của một thao tác. Tên lớp được tạo sẽ thêm "Hướng" vào tên lớp đích ban đầu. Ví dụ: nếu đích ban đầu có tên là SpecifyAmountFragment
, thì lớp được tạo sẽ có tên là SpecifyAmountFragmentDirections
.
Lớp được tạo chứa một phương thức tĩnh cho mỗi tác vụ đã quy định trong đích đến ban đầu. Phương thức này lấy các thông số tác vụ đã quy định làm đối số và trả về đối tượng NavDirections
mà bạn có thể chuyển trực tiếp đến navigate()
.
Ví dụ về Safe Args
Ví dụ: giả sử chúng ta có biểu đồ điều hướng với một tác vụ kết nối hai đích đến là SpecifyAmountFragment
và ConfirmationFragment
.
ConfirmationFragment
lấy một thông số float
duy nhất mà bạn cung cấp trong tác vụ.
Safe Args tạo một lớp SpecifyAmountFragmentDirections
bằng một phương thức duy nhất là actionSpecifyAmountFragmentToConfirmationFragment()
và một lớp bên trong có tên là ActionSpecifyAmountFragmentToConfirmationFragment
. Lớp bên trong được lấy từ NavDirections
và lưu trữ mã thao tác cũng như tham số float
được liên kết. Sau đó, đối tượng NavDirections
được trả về có thể được chuyển trực tiếp đến navigate()
, như trong ví dụ sau:
Kotlin
override fun onClick(v: View) { val amount: Float = ... val action = SpecifyAmountFragmentDirections .actionSpecifyAmountFragmentToConfirmationFragment(amount) v.findNavController().navigate(action) }
Java
@Override public void onClick(View view) { float amount = ...; action = SpecifyAmountFragmentDirections .actionSpecifyAmountFragmentToConfirmationFragment(amount); Navigation.findNavController(view).navigate(action); }
Để biết thêm thông tin về việc truyền dữ liệu giữa các đích đến bằng Safe Args, hãy xem phần Sử dụng Safe Args để truyền dữ liệu với an toàn kiểu.
Sử dụng mã nhận dạng để điều hướng
navigate(int)
lấy mã tài nguyên của một tác vụ hoặc một đích đến. Đoạn mã sau đây cho biết cách chuyển đến ViewTransactionsFragment
:
Kotlin
viewTransactionsButton.setOnClickListener { view -> view.findNavController().navigate(R.id.viewTransactionsAction) }
Java
viewTransactionsButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { Navigation.findNavController(view).navigate(R.id.viewTransactionsAction); } });
Có ba biến thể Navigation.createNavigateOnClickListener()
của các nút.
Những biến thể này khá hữu ích nếu bạn sử dụng ngôn ngữ lập trình Java. Nếu bạn sử dụng Kotlin, OnClickListener
là một giao diện SAM, vì vậy bạn có thể sử dụng hàm lambda tạo vệt. Hàm này có thể ngắn và dễ đọc hơn so với việc gọi trực tiếpcreateNavigateOnClickListener()
.
Để xử lý các thành phần giao diện người dùng phổ biến khác, chẳng hạn như thanh ứng dụng trên cùng và thanh điều hướng dưới cùng, hãy xem Cập nhật thành phần giao diện người dùng bằng NavigationUI.
Cung cấp các tuỳ chọn điều hướng đối với thao tác
Khi xác định một thao tác trong biểu đồ điều hướng, tính năng Điều hướng sẽ tạo một lớp NavAction
tương ứng, trong đó có chứa cấu hình được xác định cho thao tác đó, bao gồm những thông tin sau đây:
- Đích đến: Mã tài nguyên của đích đến.
- Đối số mặc định:
android.os.Bundle
chứa giá trị mặc định cho đích đến nếu được cung cấp. - Tuỳ chọn điều hướng: Tuỳ chọn điều hướng được biểu thị dưới dạng
NavOptions
. Lớp này chứa toàn bộ cấu hình đặc biệt để chuyển đổi qua lại từ đích đến mục tiêu, bao gồm cả cấu hình tài nguyên của ảnh động, hành vi bật lên lẫn việc bạn có nên khởi chạy đích đến ở chế độ trên cùng duy nhất hay không.
Hãy cùng tìm hiểu biểu đồ ví dụ gồm hai màn hình với tác vụ để điều hướng từ màn hình này sang màn hình khác:
<?xml version="1.0" encoding="utf-8"?>
<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/nav_graph"
app:startDestination="@id/a">
<fragment android:id="@+id/a"
android:name="com.example.myapplication.FragmentA"
android:label="a"
tools:layout="@layout/a">
<action android:id="@+id/action_a_to_b"
app:destination="@id/b"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"/>
</fragment>
<fragment android:id="@+id/b"
android:name="com.example.myapplication.FragmentB"
android:label="b"
tools:layout="@layout/b">
<action android:id="@+id/action_b_to_a"
app:destination="@id/a"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"/>
</fragment>
</navigation>
Khi đồ thị điều hướng tăng cường, các thao tác này sẽ được phân tích cú pháp và đối tượng NavAction
tương ứng sẽ được tạo bằng cấu hình đã xác định trong biểu đồ. Ví dụ: action_b_to_a
được xác định sẽ đi theo đường dẫn từ đích b
đến a
. Tác vụ này bao gồm các ảnh động với hành vi popTo
giúp xóa toàn bộ đích đến khỏi ngăn xếp ngược. Tất cả chế độ cài đặt này đều được ghi lại dưới dạng NavOptions
và gắn liền với NavAction
.
Để theo dõi NavAction
này, hãy sử dụng NavController.navigate()
, truyền mã thao tác như minh hoạ trong ví dụ sau đây:
Kotlin
findNavController().navigate(R.id.action_b_to_a)
Java
NavigationHostFragment.findNavController(this).navigate(R.id.action_b_to_a);
Áp dụng NavOptions theo phương thức lập trình
Các ví dụ trước cho thấy cách chỉ định NavOptions
trong XML của biểu đồ điều hướng. Tuy nhiên, các tuỳ chọn cụ thể có thể khác nhau tuỳ thuộc vào những hạn chế chưa xác định tại thời gian xây dựng. Trong trường hợp đó, bạn phải tạo và đặt NavOptions
theo lập trình, như minh hoạ trong ví dụ sau đây:
Kotlin
findNavController().navigate( R.id.action_fragmentOne_to_fragmentTwo, null, navOptions { // Use the Kotlin DSL for building NavOptions anim { enter = android.R.animator.fade_in exit = android.R.animator.fade_out } } )
Java
NavController navController = NavHostFragment.findNavController(this); navController.navigate( R.id.action_fragmentOne_to_fragmentTwo, null, new NavOptions.Builder() .setEnterAnim(android.R.animator.fade_in) .setExitAnim(android.R.animator.fade_out) .build() );
Ví dụ này sử dụng dạng navigate()
mở rộng, đồng thời chứa thêm các đối số Bundle
và NavOptions
. Tất cả các biến thể của navigate()
đều có phiên bản mở rộng chấp nhận đối số NavOptions
.
Ngoài ra còn có thể áp dụng NavOptions
theo phương thức lập trình khi điều hướng đến đường liên kết sâu:
Kotlin
findNavController().navigate( deepLinkUri, navOptions { // Use the Kotlin DSL for building NavOptions anim { enter = android.R.animator.fade_in exit = android.R.animator.fade_out } } )
Java
NavController navController = NavHostFragment.findNavController(this); navController.navigate( deepLinkUri, new NavOptions.Builder() .setEnterAnim(android.R.animator.fade_in) .setExitAnim(android.R.animator.fade_out) .build() );
Biến thể này của navigate()
sẽ lấy Uri
cho đường liên kết sâu, cũng như thực thể NavOptions
.
Điều hướng bằng DeepLinkRequest
Bạn có thể sử dụng navigate(NavDeepLinkRequest)
để chuyển thẳng tới đích đến chứa đường liên kết sâu ngầm ẩn, như minh hoạ trong ví dụ sau đây:
Kotlin
val request = NavDeepLinkRequest.Builder .fromUri("android-app://androidx.navigation.app/profile".toUri()) .build() findNavController().navigate(request)
Java
NavDeepLinkRequest request = NavDeepLinkRequest.Builder .fromUri(Uri.parse("android-app://androidx.navigation.app/profile")) .build() NavHostFragment.findNavController(this).navigate(request)
Ngoài Uri
,NavDeepLinkRequest
cũng hỗ trợ liên kết sâu với tác vụ và loại MIME. Để thêm tác vụ vào yêu cầu, hãy sử dụng fromAction()
hoặc setAction()
.
Để thêm loại MIME vào yêu cầu, hãy sử dụng fromMimeType()
hoặc setMimeType()
.
Để NavDeepLinkRequest
khớp với đích đến ngầm của đường liên kết sâu thì URI, tác vụ và MIME phải khớp với NavDeepLink
trong trang đích. URI phải khớp mẫu, các tác vụ phải khớp chính xác và loại MIME phải kết nối với nhau (ví dụ: "image/jpg" khớp với "image/*").
Không giống như cách điều hướng bằng mã thao tác hoặc mã đích đến, bạn có thể chuyển đến bất kỳ đường liên kết sâu nào trong biểu đồ, cho dù đích đến đó có hiển thị hay không. Bạn có thể điều hướng đến một đích đến trên biểu đồ hiện tại hoặc trên biểu đồ hoàn toàn khác.
Khi điều hướng bằng NavDeepLinkRequest
, ngăn xếp lui sẽ không được đặt lại. Hành vi này không giống như điều hướng liên kết sâu khác, trong đó ngăn xếp lui được thay thế khi điều hướng. popUpTo
và popUpToInclusive
vẫn xoá đích đến khỏi ngăn xếp lui giống như khi điều hướng bằng mã nhận dạng.
Thao tác và ngăn xếp lui
Android duy trì một ngăn xếp lui chứa các đích đến mà bạn đã truy cập. Đích đến đầu tiên được đặt trên ngăn xếp khi người dùng mở ứng dụng. Mỗi lệnh gọi đến phương thức navigate()
sẽ đặt một đích đến khác lên trên ngăn xếp. Thao tác nhấn vào Up (Mũi tên lên) hoặc Back (Quay lại) sẽ gọi NavController.navigateUp()
và NavController.popBackStack()
, lần lượt xoá phương thức (hoặc pop (bật lên)) đích đến trên đầu khỏi ngăn xếp.
NavController.popBackStack()
trả về một boolean cho biết thuộc tính này có quay lại thành công một điểm khác hay không. Trường hợp phổ biến nhất là khi phương thức này trả về false
cũng là lúc bạn thao tác để đích đến bắt đầu của biểu đồ bật lên.
Khi phương thức này trả về false
, NavController.getCurrentDestination()
sẽ trả về null
. Bạn chịu trách nhiệm việc điều hướng đến đích đến mới hoặc xử lý cửa sổ bật lên bằng cách gọi finish()
trên Tác vụ của bạn, như minh họa trong ví dụ sau:
Kotlin
... if (!navController.popBackStack()) { // Call finish() on your Activity finish() }
Java
... if (!navController.popBackStack()) { // Call finish() on your Activity finish(); }
đích đến của hộp thoại triển khai giao diện FloatingWindow
, cho biết lớp phủ này che các đích đến khác trong ngăn xếp lui. Do đó, một hoặc nhiều đích FloatingWindow
chỉ có thể hiển thị ở phía trên cùng của ngăn xếp lui điều hướng. Khi điều hướng đến một đích đến không triển khai FloatingWindow
, hệ thống sẽ tự động bật tất cả các đích đến FloatingWindow
ở phía trên cùng của ngăn xếp. Nhờ vậy, đích đến hiện tại luôn hiển thị đầy đủ phía trên các đích đến khác trong ngăn xếp lui.
Ví dụ: nếu ngăn xếp lui chỉ bao gồm các đích đến không nổi và người dùng di chuyển tới một đích đến Dialog
, thì ngăn xếp lui sẽ có dạng tương tự như hình 1:

Dialog
ở trên cùng.Sau đó, nếu người dùng di chuyển đến một đích khác của Dialog
, thì đích này sẽ được thêm vào đầu ngăn xếp lui, như trong hình 2:

Dialog
ở trên.Nếu sau đó người dùng di chuyển tới một đích đến không nổi, thì đích đến FloatingWindow
sẽ bật lên từ đầu ngăn xếp lui trước rồi mới chuyển tới đích đến mới, như minh hoạ trong hình 3:

Dialog
bật ra và đích đến mới sẽ được thêm vào.popUpTo và popUpToInclusive
Khi điều hướng bằng thao tác, bạn có thể tuỳ ý bật các đích đến khác lên khỏi ngăn xếp lui. Ví dụ: nếu ứng dụng có luồng đăng nhập ban đầu, thì khi người dùng đã đăng nhập, bạn nên bật mọi đích đến liên quan tới hoạt động đăng nhập lên khỏi ngăn xếp lui để nút Back (Quay lại) không đưa người dùng trở về luồng đăng nhập đó.
Để bật lên các đích đến khi di chuyển từ đích đến này tới đích đến khác, vui lòng thêm thuộc tính app:popUpTo
vào phần tử <action>
đã liên kết. app:popUpTo
yêu cầu thư viện Navigation (Điều hướng) bật lên một số đích đến khỏi ngăn xếp lui như một phần của lệnh gọi tới navigate()
. Giá trị thuộc tính là mã nhận dạng của đích đến gần đây nhất sẽ nằm trên ngăn xếp.
Bạn cũng có thể thêm app:popUpToInclusive="true"
để biểu thị rằng đích đến đã chỉ định trong app:popUpTo
cũng nên được xoá khỏi ngăn xếp lui.
Ví dụ về popUpTo: logic hình tròn
Giả sử ứng dụng có 3 đích đến là A, B và C, cùng với các tác vụ dẫn từ A đến B, B đến C và C quay lại A. Biểu đồ điều hướng tương ứng được hiển thị trong hình 4:
Hình 4. Một biểu đồ điều hướng hình tròn có 3 đích đến là A, B và C.
Với mỗi tác vụ điều hướng, một đích đến sẽ được thêm vào ngăn xếp lui. Nếu bạn phải di chuyển liên tục qua luồng này, thì ngăn xếp lui sẽ chứa nhiều tập hợp các đích đến (A, B, C, A, B, C, A, v.v.).
Để tránh việc lặp lại này, bạn có thể chỉ định app:popUpTo
và app:popUpToInclusive
trong thao tác đưa từ đích đến C tới đích đến A, như minh hoạ trong ví dụ sau đây:
<fragment android:id="@+id/c" android:name="com.example.myapplication.C" android:label="fragment_c" tools:layout="@layout/fragment_c"> <action android:id="@+id/action_c_to_a" app:destination="@id/a" app:popUpTo="@+id/a" app:popUpToInclusive="true"/> </fragment>
Sau khi đến đích C, ngăn xếp lui chứa một phiên bản của từng đích đến (A, B, C). Khi quay lại đích đến A, chúng ta cũng vẫn popUpTo
A, nghĩa là xoá B và C khỏi ngăn xếp trong quá trình điều hướng. Chúng ta cũng loại bỏ ô A đầu tiên khỏi ngăn xếp vớiapp:popUpToInclusive="true"
một cách hiệu quả. Lưu ý rằng nếu không sử dụngapp:popUpToInclusive
, ngăn xếp lui của bạn sẽ chứa hai phiên bản của đích đến A.
popUpToSaveState và restoreSaveState
Khi sử dụng app:popUpTo
để di chuyển tới một đích đến, thành phần Navigation 2.4.0-alpha01 trở lên cho phép bạn tuỳ ý lưu trạng thái của mọi đích đến bật lên khỏi ngăn xếp lui. Để bật tuỳ chọn này, hãy thêm app:popUpToSaveState="true"
vào phần tử <action>
đã liên kết:
<action
android:id="@+id/action_c_to_a"
app:destination="@id/a"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"
app:popUpToSaveState="true"/>
Khi điều hướng đến một đích đến, bạn cũng có thể thêm app:restoreSaveState="true"
để tự động khôi phục trạng thái liên kết với đích đến trong app:destination
.