Tìm hiểu và triển khai các kiến thức cơ bản

Điều hướng mô tả cách người dùng di chuyển trong ứng dụng. Người dùng tương tác với các thành phần trên giao diện người dùng, thường là bằng cách nhấn hoặc nhấp vào các thành phần đó và ứng dụng phản hồi bằng cách hiển thị nội dung mới. Nếu muốn quay lại nội dung trước đó, người dùng sẽ sử dụng cử chỉ quay lại hoặc nhấn vào nút quay lại.

Mô hình hoá trạng thái điều hướng

Một cách thuận tiện để lập mô hình hành vi này là sử dụng ngăn xếp nội dung. Khi người dùng điều hướng tiến đến nội dung mới, nội dung đó sẽ được đẩy lên đầu ngăn xếp. Khi người dùng quay lại từ nội dung đó, nội dung đó sẽ bị đẩy ra khỏi ngăn xếp và nội dung trước đó sẽ hiển thị. Trong các thuật ngữ điều hướng, ngăn xếp này thường được gọi là ngăn xếp lui vì nó đại diện cho nội dung mà người dùng có thể quay lại.

Nút hành động trên bàn phím phần mềm (biểu tượng dấu kiểm) được khoanh tròn màu đỏ.
Hình 1. Sơ đồ cho thấy cách ngăn xếp lui thay đổi theo các sự kiện điều hướng của người dùng.

Tạo ngăn xếp lui

Trong Navigation 3, ngăn xếp lui không thực sự chứa nội dung. Thay vào đó, tệp này chứa các tệp tham chiếu đến nội dung, được gọi là khoá. Khoá có thể là bất kỳ loại nào nhưng thường là các lớp dữ liệu đơn giản, có thể chuyển đổi tuần tự. Việc sử dụng tệp tham chiếu thay vì nội dung mang lại các lợi ích sau:

  • Bạn có thể dễ dàng điều hướng bằng cách đẩy các phím vào ngăn xếp lui.
  • Miễn là các khoá có thể chuyển đổi tuần tự, ngăn xếp lui có thể được lưu vào bộ nhớ cố định, cho phép ngăn xếp lui tồn tại sau khi thay đổi cấu hình và xử lý sự cố. Điều này rất quan trọng vì người dùng muốn rời khỏi ứng dụng, quay lại ứng dụng sau và tiếp tục từ nơi họ đã dừng lại với cùng nội dung đang hiển thị. Hãy xem phần Lưu ngăn xếp lui để biết thêm thông tin.

Một khái niệm chính trong API Navigation 3 là bạn sở hữu ngăn xếp lui. Thư viện:

  • Dự kiến ngăn xếp lui sẽ là List<T> được sao lưu trạng thái tổng quan nhanh, trong đó T là loại ngăn xếp lui keys. Bạn có thể sử dụng Any hoặc cung cấp các khoá có kiểu mạnh hơn của riêng mình. Khi bạn thấy các thuật ngữ "push" hoặc "pop", cách triển khai cơ bản là thêm hoặc xoá các mục khỏi cuối danh sách.
  • Quan sát ngăn xếp lui và phản ánh trạng thái của ngăn xếp lui trong giao diện người dùng bằng cách sử dụng NavDisplay.

Ví dụ sau đây cho biết cách tạo khoá và ngăn xếp lui, đồng thời sửa đổi ngăn xếp lui để phản hồi các sự kiện điều hướng của người dùng:

// Define keys that will identify content
data object ProductList
data class ProductDetail(val id: String)

@Composable
fun MyApp() {

    // Create a back stack, specifying the key the app should start with
    val backStack = remember { mutableStateListOf<Any>(ProductList) }

    // Supply your back stack to a NavDisplay so it can reflect changes in the UI
    // ...more on this below...

    // Push a key onto the back stack (navigate forward), the navigation library will reflect the change in state
    backStack.add(ProductDetail(id = "ABC"))

    // Pop a key off the back stack (navigate back), the navigation library will reflect the change in state
    backStack.removeLastOrNull()
}

Giải quyết khoá đến nội dung

Nội dung được mô hình hoá trong Navigation 3 bằng NavEntry, đây là một lớp chứa hàm có khả năng kết hợp. Thành phần này đại diện cho một đích đến – một nội dung mà người dùng có thể chuyển đếnquay lại.

NavEntry cũng có thể chứa siêu dữ liệu – thông tin về nội dung. Các đối tượng vùng chứa (như NavDisplay) có thể đọc siêu dữ liệu này để giúp quyết định cách hiển thị nội dung của NavEntry. Ví dụ: siêu dữ liệu có thể được dùng để ghi đè ảnh động mặc định cho một NavEntry cụ thể. NavEntry metadata là một bản đồ ánh xạ các khoá String đến các giá trị Any, cung cấp bộ nhớ dữ liệu linh hoạt.

Để chuyển đổi key thành NavEntry, hãy tạo entryProvider. Đây là một hàm chấp nhận key và trả về NavEntry cho key đó. Thông số này thường được xác định là tham số lambda khi tạo NavDisplay.

Có hai cách để tạo entryProvider, đó là tạo trực tiếp hàm lambda hoặc sử dụng DSL entryProvider.

Tạo trực tiếp hàm entryProvider

Thông thường, bạn tạo hàm entryProvider bằng câu lệnh when, với một nhánh cho mỗi khoá.

entryProvider = { key ->
    when (key) {
        is ProductList -> NavEntry(key) { Text("Product List") }
        is ProductDetail -> NavEntry(
            key,
            metadata = mapOf("extraDataKey" to "extraDataValue")
        ) { Text("Product ${key.id} ") }

        else -> {
            NavEntry(Unit) { Text(text = "Invalid Key: $it") }
        }
    }
}

Sử dụng DSL entryProvider

DSL entryProvider có thể đơn giản hoá hàm lambda của bạn bằng cách tránh việc cần phải kiểm thử theo từng loại khoá và tạo một NavEntry cho từng loại khoá. Sử dụng hàm tạo entryProvider cho việc này. Phương thức này cũng bao gồm hành vi dự phòng mặc định (gửi lỗi) nếu không tìm thấy khoá.

entryProvider = entryProvider {
    entry<ProductList> { Text("Product List") }
    entry<ProductDetail>(
        metadata = mapOf("extraDataKey" to "extraDataValue")
    ) { key -> Text("Product ${key.id} ") }
}

Lưu ý những điều sau trong đoạn mã:

  • entry được dùng để xác định NavEntry với loại và nội dung có thể kết hợp nhất định
  • entry chấp nhận tham số metadata để đặt NavEntry.metadata

Hiển thị ngăn xếp lui

Ngăn xếp lui thể hiện trạng thái điều hướng của ứng dụng. Bất cứ khi nào ngăn xếp lui thay đổi, giao diện người dùng của ứng dụng sẽ phản ánh trạng thái ngăn xếp lui mới. Trong Navigation 3, NavDisplay quan sát ngăn xếp lui và cập nhật giao diện người dùng cho phù hợp. Tạo lớp này bằng các tham số sau:

  • Ngăn xếp lui – ngăn xếp này phải thuộc loại SnapshotStateList<T>, trong đó T là loại của các khoá ngăn xếp lui. Đây là một List có thể quan sát được để kích hoạt quá trình kết hợp lại NavDisplay khi giá trị này thay đổi.
  • entryProvider để chuyển đổi các khoá trong ngăn xếp lui thành NavEntry.
  • Không bắt buộc, hãy cung cấp một hàm lambda cho tham số onBack. Phương thức này được gọi khi người dùng kích hoạt một sự kiện quay lại.

Ví dụ sau đây cho thấy cách tạo NavDisplay.

data object Home
data class Product(val id: String)

@Composable
fun NavExample() {

    val backStack = remember { mutableStateListOf<Any>(Home) }

    NavDisplay(
        backStack = backStack,
        onBack = { backStack.removeLastOrNull() },
        entryProvider = { key ->
            when (key) {
                is Home -> NavEntry(key) {
                    ContentGreen("Welcome to Nav3") {
                        Button(onClick = {
                            backStack.add(Product("123"))
                        }) {
                            Text("Click to navigate")
                        }
                    }
                }

                is Product -> NavEntry(key) {
                    ContentBlue("Product ${key.id} ")
                }

                else -> NavEntry(Unit) { Text("Unknown route") }
            }
        }
    )
}

Theo mặc định, NavDisplay hiển thị NavEntry trên cùng trong ngăn xếp lui theo bố cục ngăn đơn. Bản ghi sau đây cho thấy ứng dụng này đang chạy:

Hành vi mặc định của `NavDisplay` với hai đích đến.
Hình 2. Hành vi mặc định của NavDisplay với hai đích đến.

Kết hợp kiến thức đã học

Sơ đồ sau đây cho thấy cách dữ liệu di chuyển giữa các đối tượng trong Navigation 3:

Hình ảnh minh hoạ cách dữ liệu di chuyển giữa các đối tượng trong Navigation 3.
Hình 3. Sơ đồ cho thấy cách dữ liệu di chuyển qua nhiều đối tượng trong Navigation 3.
  1. Sự kiện điều hướng bắt đầu thay đổi. Các khoá được thêm hoặc xoá khỏi ngăn xếp lui để phản hồi tương tác của người dùng.

  2. Thay đổi trạng thái ngăn xếp lui sẽ kích hoạt quá trình truy xuất nội dung. NavDisplay (một thành phần kết hợp hiển thị ngăn xếp lui) quan sát ngăn xếp lui. Trong cấu hình mặc định, ngăn này hiển thị mục nhập ngăn xếp lui trên cùng trong một bố cục ngăn. Khi khoá trên cùng trên ngăn xếp lui thay đổi, NavDisplay sẽ sử dụng khoá này để yêu cầu nội dung tương ứng từ trình cung cấp mục nhập.

  3. Trình cung cấp mục cung cấp nội dung. Nhà cung cấp mục nhập là một hàm phân giải khoá thành NavEntry. Khi nhận được khoá từ NavDisplay, trình cung cấp mục nhập sẽ cung cấp NavEntry được liên kết, chứa cả khoá và nội dung.

  4. Nội dung hiển thị. NavDisplay nhận NavEntry và hiển thị nội dung.