Thành phần Điều hướng cho phép bạn đính kèm dữ liệu vào một thao tác điều hướng bằng cách xác định các đối số cho một đích đến. Ví dụ: đích hồ sơ người dùng có thể sử dụng một đối số mã nhận dạng người dùng để xác định xem cần cho thấy người dùng nào.
Nhìn chung, bạn chỉ nên truyền lượng dữ liệu tối thiểu giữa các đích. Ví dụ: bạn nên truyền một khoá để truy xuất một đối tượng thay vì truyền chính đối tượng đó, vì tổng dung lượng cho tất cả trạng thái đã lưu sẽ bị giới hạn trên Android. Nếu bạn cần truyền một lượng lớn dữ liệu, hãy dùng ViewModel
như mô tả trong phần Tổng quan về ViewModel.
Xác định đối số đích
Để truyền dữ liệu giữa các đích, trước tiên, hãy xác định đối số bằng cách thêm đối số đó vào đích nhận dữ liệu đó theo các bước sau:
- Trong Navigation Editor (Trình chỉnh sửa điều hướng), hãy nhấp vào đích nhận đối số.
- Trong bảng Attributes (Thuộc tính), hãy nhấp vào Add (Thêm) (+).
- Trong cửa sổ Add Argument Link (Thêm đường liên kết đối số) hiện ra, hãy nhập tên đối số, loại đối số, liệu đối số có thể rỗng hay không và giá trị mặc định (nếu cần).
- Nhấp vào Add (Thêm). Lưu ý rằng bây giờ, đối số sẽ xuất hiện trong danh sách Arguments (Đối số) trong bảng Attributes (Thuộc tính).
- Tiếp theo, hãy nhấp vào thao tác tương ứng để đưa bạn đến đích này. Trong bảng Attributes (Thuộc tính), bây giờ, bạn sẽ thấy đối số mới được thêm vào trong phần Argument Default Values (Giá trị mặc định của đối số).
Bạn cũng có thể thấy rằng đối số đã được thêm vào XML. Nhấp vào thẻ Text (Văn bản) để chuyển sang khung hiển thị XML. Bạn sẽ nhận thấy đối số đã được thêm vào đích nhận đối số. Sau đây là ví dụ minh hoạ:
<fragment android:id="@+id/myFragment" > <argument android:name="myArg" app:argType="integer" android:defaultValue="0" /> </fragment>
Kiểu đối số được hỗ trợ
Thư viện điều hướng (Navigation library) hỗ trợ các loại đối số sau:
Loại | cú pháp app:argType | Hỗ trợ giá trị mặc định | Xử lý theo tuyến | Có thể rỗng |
---|---|---|---|---|
Số nguyên | app:argType="integer" | Có | Có | Không |
Độ chính xác đơn | app:argType="float" | Có | Có | Không |
Dài | app:argType="long" | Có – Giá trị mặc định phải luôn kết thúc bằng hậu tố "L" (ví dụ: "123L"). | Có | Không |
Boolean | app:argType="boolean" | Có – "true" hoặc "false" | Có | Không |
Chuỗi | app:argType="string" | Có | Có | Có |
Mã tham chiếu tài nguyên | app:argType="reference" | Có – Giá trị mặc định phải có dạng "@resourceType/resourceName" (ví dụ: "@style/myCustomStyle") hoặc "0" | Có | Không |
Theo gói tuỳ chỉnh | app:argType="<type>", trong đó <type> là tên lớp đủ điều kiện của Parcelable |
Hỗ trợ giá trị mặc định "@null". Không hỗ trợ các giá trị mặc định khác. | Không | Có |
Theo tuần tự tuỳ chỉnh | app:argType="<type>", trong đó <type> là tên lớp đủ điều kiện của Serializable |
Hỗ trợ giá trị mặc định "@null". Không hỗ trợ các giá trị mặc định khác. | Không | Có |
Enum tuỳ chỉnh | app:argType="<type>", trong đó <type> là tên đủ điều kiện của enum | Có – Các giá trị mặc định phải khớp với tên chưa đủ điều kiện (ví dụ: "SUCCESS" để khớp với MyEnum.SUCCESS). | Không | Không |
Nếu có một loại đối số hỗ trợ giá trị rỗng, thì bạn có thể khai báo một giá trị mặc định là rỗng bằng cách sử dụng android:defaultValue="@null"
.
Các tuyến, đường liên kết sâu và URI kèm theo đối số có thể được phân tích cú pháp trên các chuỗi. Bạn không thể thực hiện việc này bằng cách sử dụng các kiểu dữ liệu tuỳ chỉnh như Theo gói và Theo tuần tự như trong bảng trên. Để truyền dữ liệu phức tạp tuỳ chỉnh, hãy lưu trữ dữ liệu ở nơi khác (chẳng hạn như ViewModel hoặc cơ sở dữ liệu) và chỉ truyền giá trị nhận dạng trong khi di chuyển; sau đó truy xuất dữ liệu ở vị trí mới sau khi quá trình di chuyển kết thúc.
Khi bạn chọn một trong số các loại tuỳ chỉnh, hộp thoại Select Class (Chọn lớp) sẽ xuất hiện và nhắc bạn chọn lớp tương ứng cho loại đó. Thẻ Project (Dự án) cho phép bạn chọn một lớp trong dự án hiện tại.
Bạn có thể chọn <inferred type> để Thư viện điều hướng xác định loại dựa trên giá trị đã cho.
Bạn có thể chọn Array (Mảng) để cho biết đối số phải là một mảng của giá trị Type (Loại) đã chọn. Xin lưu ý những điều sau:
- Hệ thống không hỗ trợ mảng enum và mảng mã tham chiếu tài nguyên.
- Mảng (array) hỗ trợ giá trị rỗng, bất kể giá trị rỗng thuộc kiểu cơ bản có được hỗ trợ hay không. Chẳng hạn, khi sử dụng
app:argType="integer[]"
, bạn có thể dùngapp:nullable="true"
để chỉ ra rằng có thể truyền một mảng rỗng. - Mảng hỗ trợ một giá trị mặc định duy nhất là "@null". Mảng không hỗ trợ giá trị mặc định nào khác.
Ghi đè đối số đích trong thao tác
Các đối số cấp đích và giá trị mặc định được sử dụng trong mọi thao tác điều hướng đến đích. Nếu cần, bạn có thể ghi đè giá trị mặc định của đối số (hoặc đặt một đối số nếu chưa có) bằng cách xác định đối số ở cấp thao tác. Đối số này phải có cùng tên và kiểu với đối số đã khai báo trong đích.
XML sau đây khai báo một thao tác có một đối số ghi đè đối số cấp đích trong ví dụ trước:
<action android:id="@+id/startMyFragment"
app:destination="@+id/myFragment">
<argument
android:name="myArg"
app:argType="integer"
android:defaultValue="1" />
</action>
Dùng Safe Args để truyền dữ liệu đảm bảo an toàn về kiểu
Thành phần điều hướng có một trình bổ trợ Gradle tên là Safe Args. Trình bổ trợ này tạo ra các lớp an toàn cho đối tượng và trình tạo để điều hướng an toàn về kiểu, đồng thời cấp quyền truy cập vào đối số bất kỳ có liên quan. Bạn nên sử dụng Safe Args để điều hướng và truyền dữ liệu, vì trình bổ trợ này đảm bảo an toàn về kiểu.
Nếu không dùng Gradle, bạn không thể sử dụng trình bổ trợ Safe Args. Trong những trường hợp như vậy, bạn có thể sử dụng Gói để trực tiếp truyền dữ liệu.
To add Safe Args
to your project, include the following classpath
in your top level build.gradle
file:
Groovy
buildscript { repositories { google() } dependencies { def nav_version = "2.8.4" classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version" } }
Kotlin
buildscript { repositories { google() } dependencies { val nav_version = "2.8.4" classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version") } }
You must also apply one of two available plugins.
To generate Java language code suitable for Java or mixed Java and Kotlin modules, add
this line to your app or module's build.gradle
file:
Groovy
plugins { id 'androidx.navigation.safeargs' }
Kotlin
plugins { id("androidx.navigation.safeargs") }
Alternatively, to generate Kotlin code suitable for Kotlin-only modules add:
Groovy
plugins { id 'androidx.navigation.safeargs.kotlin' }
Kotlin
plugins { id("androidx.navigation.safeargs.kotlin") }
You must have android.useAndroidX=true
in your
gradle.properties
file as per
Migrating to AndroidX.
Sau khi bật Safe Args, mã do bạn tạo sẽ chứa các phương thức và lớp an toàn về kiểu sau đây cho mỗi thao tác cũng như mỗi đích gửi và nhận.
Một lớp được tạo cho mỗi đích nơi bắt nguồn một hành động. Tên của lớp này là tên của đích ban đầu, nối thêm từ "Directions" (Hướng). Ví dụ: nếu đích xuất phát là một mảnh có tên là
SpecifyAmountFragment
, thì lớp được tạo sẽ gọi làSpecifyAmountFragmentDirections
.Lớp này có một phương thức cho từng thao tác được xác định trong đích ban đầu.
Đối với từng thao tác dùng để truyền đối số, một lớp bên trong được tạo sẽ có tên dựa trên thao tác đó. Ví dụ: nếu thao tác gọi là
confirmationAction,
, thì lớp được đặt tên làConfirmationAction
. Nếu thao tác của bạn chứa các đối số mà không códefaultValue
, thì bạn sẽ sử dụng lớp thao tác liên kết để đặt giá trị cho các đối số đó.Một lớp được tạo cho đích nhận. Tên của lớp này là tên của đích, nối thêm từ "Args". Ví dụ: nếu mảnh đích có tên
ConfirmationFragment,
, thì lớp được tạo sẽ gọi làConfirmationFragmentArgs
. Hãy sử dụng phương thứcfromBundle()
của lớp này để truy xuất các đối số.
Ví dụ sau đây cho thấy cách sử dụng những phương thức này để đặt một đối số và truyền đối số đó đến phương thức navigate()
:
Kotlin
override fun onClick(v: View) { val amountTv: EditText = view!!.findViewById(R.id.editTextAmount) val amount = amountTv.text.toString().toInt() val action = SpecifyAmountFragmentDirections.confirmationAction(amount) v.findNavController().navigate(action) }
Java
@Override public void onClick(View view) { EditText amountTv = (EditText) getView().findViewById(R.id.editTextAmount); int amount = Integer.parseInt(amountTv.getText().toString()); ConfirmationAction action = SpecifyAmountFragmentDirections.confirmationAction(); action.setAmount(amount); Navigation.findNavController(view).navigate(action); }
Trong mã cho đích nhận, hãy sử dụng phương thức getArguments()
để truy xuất gói và dùng nội dung của gói. Khi sử dụng các phần phụ thuộc -ktx
, người dùng Kotlin cũng có thể sử dụng mã uỷ quyền thuộc tính by navArgs()
để truy cập các đối số.
Kotlin
val args: ConfirmationFragmentArgs by navArgs() override fun onViewCreated(view: View, savedInstanceState: Bundle?) { val tv: TextView = view.findViewById(R.id.textViewAmount) val amount = args.amount tv.text = amount.toString() }
Java
@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { TextView tv = view.findViewById(R.id.textViewAmount); int amount = ConfirmationFragmentArgs.fromBundle(getArguments()).getAmount(); tv.setText(amount + ""); }
Sử dụng Safe Args bằng thao tác chung
Khi dùng thao tác chung trên Safe Args, bạn phải cung cấp giá trị android:id
cho phần tử <navigation>
gốc, như trong ví dụ sau:
<?xml version="1.0" encoding="utf-8"?> <navigation xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/main_nav" app:startDestination="@id/mainFragment"> ... </navigation>
Thành phần điều hướng sẽ tạo một lớp Directions
cho phần tử <navigation>
dựa trên giá trị android:id
. Ví dụ: nếu bạn có một phần tử <navigation>
có android:id=@+id/main_nav
, lớp được tạo sẽ gọi là MainNavDirections
. Tất cả đích trong phần tử <navigation>
đều đã tạo ra các phương thức để truy cập vào tất cả thao tác liên kết chung bằng chính phương thức nêu trong phần trước.
Truyền dữ liệu giữa các đích bằng đối tượng Bundle
Nếu không sử dụng Gradle, bạn vẫn có thể truyền đối số giữa các đích bằng cách sử dụng đối tượng Bundle
. Tạo một đối tượng Bundle
và truyền đối tượng đó đến đích bằng cách sử dụng navigate()
, như trong ví dụ sau:
Kotlin
val bundle = bundleOf("amount" to amount) view.findNavController().navigate(R.id.confirmationAction, bundle)
Java
Bundle bundle = new Bundle(); bundle.putString("amount", amount); Navigation.findNavController(view).navigate(R.id.confirmationAction, bundle);
Trong mã của đích nhận, hãy sử dụng phương thức getArguments()
để truy xuất Bundle
và sử dụng nội dung trong đó.
Kotlin
val tv = view.findViewById<TextView>(R.id.textViewAmount) tv.text = arguments?.getString("amount")
Java
TextView tv = view.findViewById(R.id.textViewAmount); tv.setText(getArguments().getString("amount"));
Truyền dữ liệu đến đích bắt đầu
Bạn có thể truyền dữ liệu đến đích bắt đầu của ứng dụng. Trước tiên, bạn phải tạo rõ ràng một Bundle
chứa dữ liệu. Tiếp theo, hãy sử dụng một trong các phương thức sau để truyền Bundle
đến đích bắt đầu:
- Nếu bạn đang tạo
NavHost
theo phương thức lập trình, hãy gọiNavHostFragment.create(R.navigation.graph, args)
, trong đóargs
làBundle
lưu trữ dữ liệu của bạn. - Nếu không, bạn có thể đặt các đối số đích bắt đầu bằng cách gọi một trong các phương thức nạp chồng sau đây của
NavController.setGraph()
:- Sử dụng mã nhận dạng của biểu đồ:
navController.setGraph(R.navigation.graph, args)
- Sử dụng chính biểu đồ:
navController.setGraph(navGraph, args)
- Sử dụng mã nhận dạng của biểu đồ:
Để truy xuất dữ liệu trong đích bắt đầu, hãy gọi Fragment.getArguments()
.
Một số điểm lưu ý về ProGuard
Nếu đang rút gọn mã, bạn cần ngăn không cho các tên lớp Parcelable
, Serializable
và Enum
bị làm rối trong quá trình rút gọn. Bạn có thể làm việc này bằng một trong hai cách:
- Sử dụng chú giải @Keep.
- Sử dụng quy tắc keepnames.
Các tiểu mục sau đây trình bày các phương pháp này.
Sử dụng chú giải @Keep
Ví dụ sau đây sẽ thêm chú giải @Keep
vào phần định nghĩa lớp mô hình:
Kotlin
@Keep class ParcelableArg : Parcelable { ... } @Keep class SerializableArg : Serializable { ... } @Keep enum class EnumArg { ... }
Java
@Keep public class ParcelableArg implements Parcelable { ... } @Keep public class SerializableArg implements Serializable { ... } @Keep public enum EnumArg { ... }
Sử dụng quy tắc keepnames
Bạn cũng có thể thêm các quy tắc keepnames
vào tệp proguard-rules.pro
, như trong ví dụ sau:
proguard-rules.pro
...
-keepnames class com.path.to.your.ParcelableArg
-keepnames class com.path.to.your.SerializableArg
-keepnames class com.path.to.your.EnumArg
...
Tài nguyên khác
Để tìm hiểu thêm về cách điều hướng, hãy tham khảo thêm những tài nguyên sau đây.