NavController
có một "ngăn xếp lui" có chứa đích đến mà người dùng
đã truy cập. Khi người dùng điều hướng tới các màn hình trong toàn bộ ứng dụng, NavController
sẽ thêm đích đến vào ngăn xếp lui và xoá đích đến khỏi ngăn xếp lui.
Với vai trò ngăn xếp, ngăn xếp lui là một cấu trúc dữ liệu "vào sau, ra trước". Chiến lược phát hành đĩa đơn
Do đó, NavController
sẽ đẩy các mục lên và đẩy các mục từ đầu
ngăn xếp.
Hành vi cơ bản
Dưới đây là những thông tin cốt lõi mà bạn nên cân nhắc liên quan đến hành vi của ngăn xếp lui:
- Đích đến đầu tiên: Khi người dùng mở ứng dụng,
NavController
sẽ đẩy đích đến đầu tiên lên đầu ngăn xếp lui. - Đẩy vào ngăn xếp: Mỗi lệnh gọi
NavController.navigate()
sẽ đẩy đích đến nhất định lên đầu ngăn xếp. - Đẩy đích đến trên cùng ra: Nhấn vào Lên hoặc Quay lại để gọi phương thức
NavController.navigateUp()
vàNavController.popBackStack()
. Các phương thức này sẽ đẩy đích đến trên cùng ra khỏi ngăn xếp. Vui lòng xem trang Nguyên tắc điều hướng để biết thêm thông tin về điểm khác biệt giữa tuỳ chọn Lên và Quay lại.
Đẩy về
Phương thức NavController.popBackStack()
sẽ cố gắng đẩy đích đến hiện tại ra khỏi ngăn xếp lui rồi điều hướng tới đích đến trước. Đây là cách hiệu quả để đưa người dùng lùi lại một bước trong quá trình điều hướng. Phương thức này trả về một giá trị boolean cho biết liệu đã đẩy thành công về đích đến đó hay chưa.
Đẩy về một đích đến cụ thể
Bạn cũng có thể dùng popBackStack()
nhằm điều hướng tới một đích đến cụ thể. Để làm vậy, hãy áp dụng một trong các phương thức nạp chồng. Có vài phương thức cho phép bạn truyền một mã nhận dạng vào, chẳng hạn như số nguyên id
hoặc một chuỗi route
. Các phương thức nạp chồng này sẽ đưa người dùng tới đích đến liên kết với một mã nhận dạng nhất định. Quan trọng là chúng giúp đẩy mọi mục trong ngăn xếp lên trên đích đến đó.
Các phương thức nạp chồng này cũng tiếp nhận giá trị boolean inclusive
. Đây là giá trị xác định liệu NavController
có nên đẩy đích đến được chỉ định ra khỏi ngăn xếp lui hay không sau khi điều hướng tới đó.
Hãy tham khảo ví dụ về đoạn mã ngắn sau:
navController.popBackStack(R.id.destinationId, true)
Ở ví dụ này, NavController
sẽ đẩy về đích đến có mã nhận dạng bằng số nguyên destinationId
. Vì giá trị của đối số inclusive
là true
nên NavController
cũng đẩy đích đến đã xác định khỏi ngăn xếp lui.
Xử lý lỗi không đẩy về được
Khi popBackStack()
trả về false
, lệnh gọi tiếp theo tới NavController.getCurrentDestination()
sẽ trả về null
. Như vậy tức là ứng dụng đã đẩy đích đến cuối cùng ra khỏi ngăn xếp lui. Trong trường hợp này, người dùng chỉ thấy
một màn hình trống.
Lỗi này có thể xảy ra trong các trường hợp sau:
popBackStack()
không đẩy bất kỳ mục nào ra khỏi ngăn xếp.popBackStack()
đã đẩy một đích đến ra khỏi ngăn xếp lui và ngăn xếp đang trống.
Để giải quyết lỗi này, bạn phải điều hướng tới một đích đến mới hoặc gọi lệnh finish()
để kết thúc hoạt động của mình. Sau đây là đoạn mã minh hoạ:
kotlin
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish()
}
java
...
if (!navController.popBackStack()) {
// Call finish() on your Activity
finish();
}
Đẩy tới một đích đến
Để xoá đích đến khỏi ngăn xếp lui khi điều hướng từ đích đến này sang đích đến khác, hãy thêm đối số popUpTo()
vào lệnh gọi hàm navigate()
liên kết. popUpTo()
yêu cầu thư viện Navigation xoá một số đích đến khỏi ngăn xếp lui để thực hiện lệnh gọi đến navigate()
. Giá trị tham số là mã nhận dạng của một đích đến trong ngăn xếp lui. Giá trị nhận dạng có thể là số nguyên id
hoặc chuỗi route
.
Bạn có thể thêm một đối số cho tham số inclusive
có giá trị true
nhằm cho biết rằng đích đến mà bạn đã chỉ định trong popUpTo()
cũng sẽ bị đẩy ra khỏi ngăn xếp lui.
Để triển khai việc này theo phương thức lập trình, hãy truyền popUpTo()
đến navigate()
dưới dạng NavOptions
sau khi đã đặt inclusive
thành true
. Cách này có tác dụng trong cả Compose và Khung hiển thị.
Lưu trạng thái khi đẩy ra
Khi sử dụng popUpTo
để điều hướng tới một đích đến, bạn có thể lưu
ngăn xếp lui và trạng thái của mọi đích đến bị kéo ra khỏi ngăn xếp lui. Bạn có thể
sau đó khôi phục ngăn xếp lui và đích đến khi điều hướng tới đích đến đó
sau này. Điều này cho phép bạn duy trì trạng thái cho một đích đến nhất định và
nhiều ngăn xếp lui.
Để thực hiện việc này theo phương thức lập trình, hãy chỉ định saveState = true
khi thêm popUpTo
vào
các tùy chọn điều hướng của mình.
Bạn cũng có thể chỉ định restoreState = true
trong các tuỳ chọn điều hướng để
tự động khôi phục ngăn xếp lui và trạng thái liên kết với
đích.
Ví dụ:
navController.navigate(
route = route,
navOptions = navOptions {
popUpTo<A>{ saveState = true }
restoreState = true
}
)
Để bật tính năng lưu và khôi phục trạng thái trong XML, hãy xác định popUpToSaveState
là true
và restoreState
dưới dạng true
lần lượt trong action
được liên kết.
Ví dụ cho XML
Dưới đây là một ví dụ về popUpTo
trong XML có sử dụng một thao tác:
<action
android:id="@+id/action_a_to_b"
app:destination="@id/b"
app:popUpTo="@+id/a"
app:popUpToInclusive="true"
app:restoreState=”true”
app:popUpToSaveState="true"/>
Ví dụ về Compose
Dưới đây là một ví dụ hoàn chỉnh tương tự trong Compose:
@Composable
fun MyAppNavHost(
modifier: Modifier = Modifier,
navController: NavHostController = rememberNavController(),
startDestination: Any = A
) {
NavHost(
modifier = modifier,
navController = navController,
startDestination = startDestination
) {
composable<A> {
DestinationA(
onNavigateToB = {
// Pop everything up to, and including, the A destination off
// the back stack, saving the back stack and the state of its
// destinations.
// Then restore any previous back stack state associated with
// the B destination.
// Finally navigate to the B destination.
navController.navigate(route = B) {
popUpTo<A> {
inclusive = true
saveState = true
}
restoreState = true
}
},
)
}
composable<B> { DestinationB(/* ... */) }
}
}
@Composable
fun DestinationA(onNavigateToB: () -> Unit) {
Button(onClick = onNavigateToB) {
Text("Go to A")
}
}
Cụ thể hơn, bạn có thể thay đổi cách gọi lệnh NavController.navigate()
theo các phương thức sau:
// Pop everything up to the destination_a destination off the back stack before
// navigating to the "destination_b" destination
navController.navigate("destination_b") {
popUpTo("destination_a")
}
// Pop everything up to and including the "destination_a" destination off
// the back stack before navigating to the "destination_b" destination
navController.navigate("destination_b") {
popUpTo("destination_a") { inclusive = true }
}
// Navigate to the "search” destination only if we’re not already on
// the "search" destination, avoiding multiple copies on the top of the
// back stack
navController.navigate("search") {
launchSingleTop = true
}
Để biết thông tin chung về cách truyền tuỳ chọn tới NavController.navigate()
, hãy xem Hướng dẫn cách sử dụng tuỳ chọn.
Đẩy ra bằng thao tác
Khi điều hướng bằng thao tác, bạn có thể tuỳ ý đẩy các đích đến khác ra 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 đẩy mọi đích đến liên quan tới hoạt động đăng nhập ra khỏi ngăn xếp lui để nút Quay lại không đưa người dùng trở về luồng đăng nhập đó.
Đọc thêm
Để biết thêm thông tin, hãy đọc các trang sau:
- Luồng điều hướng vòng tròn: Tìm hiểu cách bạn có thể tránh tình trạng ngăn xếp lui bị quá tải trong trường hợp luồng điều hướng đi theo vòng tròn.
- Đích đến của hộp thoại: Tìm hiểu cách các đích đến của hộp thoại đưa ra những điểm cần cân nhắc riêng cho việc quản lý ngăn xếp lui.