Biểu đồ lồng ghép

Các luồng đăng nhập, trình hướng dẫn hoặc luồng phụ khác trong ứng dụng của bạn thường được biểu thị tốt nhất dưới dạng biểu đồ điều hướng lồng nhau. Bằng cách lồng những luồng điều hướng phụ độc lập theo cách này, luồng chính của giao diện người dùng trong ứng dụng của bạn sẽ dễ hiểu và dễ quản lý hơn.

Ngoài ra, các biểu đồ lồng nhau có thể sử dụng lại được. Các bảng này cũng đưa ra một cấp độ đóng gói – các đích đến nằm ngoài biểu đồ lồng nhau không có quyền truy cập trực tiếp vào bất kỳ đích đến nào bên trong biểu đồ lồng nhau đó. Thay vào đó, các đích đến này phải navigate() đến chính biểu đồ lồng nhau, trong đó logic bên trong có thể thay đổi mà không ảnh hưởng đến phần còn lại của biểu đồ.

Ví dụ

Biểu đồ điều hướng cấp cao nhất (top-level) của ứng dụng phải bắt đầu với đích đến đầu tiên mà người dùng nhìn thấy khi khởi chạy ứng dụng và phải bao gồm các đích đến mà họ nhìn thấy khi di chuyển trong ứng dụng của bạn.

Hình 1. Biểu đồ điều hướng cấp cao nhất.

Sử dụng biểu đồ điều hướng cấp cao nhất trong hình 1 làm ví dụ, giả sử bạn muốn yêu cầu người dùng xem màn hình title_screen và màn hình đăng ký chỉ khi ứng dụng khởi chạy lần đầu tiên. Sau đó, thông tin người dùng sẽ được lưu trữ. Trong các lần chạy ứng dụng tiếp theo, bạn nên đưa họ thẳng đến màn hình so khớp.

Cách tốt nhất là bạn nên đặt màn hình so khớp làm đích đến khởi đầu của biểu đồ điều hướng cấp cao nhất, đồng thời chuyển màn hình tiêu đề và màn hình đăng ký vào trong một biểu đồ lồng nhau, như minh hoạ trong hình 2:

Hình 2. Biểu đồ điều hướng cấp cao nhất hiện chứa một biểu đồ lồng nhau.

Khi màn hình so khớp chạy, hãy kiểm tra xem có người dùng đã đăng ký nào không. Nếu người dùng chưa đăng ký, hãy chuyển người dùng đến màn hình đăng ký.

Để biết thêm thông tin về tình huống điều hướng có điều kiện, vui lòng xem phần Điều hướng có điều kiện.

Compose

Để tạo biểu đồ điều hướng lồng nhau bằng Compose, hãy sử dụng hàm NavGraphBuilder.navigation(). Bạn sử dụng navigation() giống như các hàm NavGraphBuilder.composable()NavGraphBuilder.dialog() khi thêm đích đến vào biểu đồ.

Điểm khác biệt chính là navigation tạo một biểu đồ lồng nhau thay vì một đích đến mới. Sau đó, bạn sẽ gọi composabledialog trong lambda của navigation để thêm đích đến vào biểu đồ lồng nhau.

Hãy xem xét cách đoạn mã sau đây triển khai biểu đồ ở hình 2 bằng cách sử dụng Compose:

NavHost(navController, startDestination = "title_screen") {
    composable("title_screen") {
        TitleScreen(
            onPlayClicked = { navController.navigate("register") },
            onLeaderboardsClicked = { /* Navigate to leaderboards */ }
        )
    }
    composable("register") {
        RegisterScreen(
            onSignUpComplete = { navController.navigate("gameInProgress") }
        )
    }
    navigation(startDestination = "match", route = "gameInProgress") {
        composable("match") {
            MatchScreen(
                onStartGame = { navController.navigate("in_game") }
            )
        }
        composable("in_game") {
            InGameScreen(
                onGameWin = { navController.navigate("results_winner") },
                onGameLose = { navController.navigate("game_over") }
            )
        }
        composable("results_winner") {
            ResultsWinnerScreen(
                onNextMatchClicked = {
                    navController.navigate("match") {
                        popUpTo("match") { inclusive = true }
                    }
                },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("game_over") {
            GameOverScreen(
                onTryAgainClicked = {
                    navController.navigate("match") {
                        popUpTo("match") { inclusive = true }
                    }
                }
            )
        }
    }
}

Để điều hướng trực tiếp đến một đích đến lồng nhau, hãy sử dụng route giống như cách bạn di chuyển đến bất kỳ đích đến nào khác. Đó là vì tuyến đường là một khái niệm chung mà mọi màn hình đều có thể điều hướng đến:

navController.navigate("match")

Hàm mở rộng

Bạn có thể thêm đích đến vào biểu đồ bằng cách sử dụng hàm mở rộng trong NavGraphBuilder. Bạn có thể sử dụng các hàm mở rộng này cùng với các phương thức mở rộng navigation, composabledialog tạo sẵn.

Ví dụ: bạn có thể sử dụng hàm mở rộng để thêm biểu đồ lồng nhau được minh hoạ ở phần trước:

fun NavGraphBuilder.addNestedGraph(navController: NavController) {
    navigation(startDestination = "match", route = "gameInProgress") {
        composable("match") {
            MatchScreen(
                onStartGame = { navController.navigate("in_game") }
            )
        }
        composable("in_game") {
            InGameScreen(
                onGameWin = { navController.navigate("results_winner") },
                onGameLose = { navController.navigate("game_over") }
            )
        }
        composable("results_winner") {
            ResultsWinnerScreen(
                onNextMatchClicked = { navController.navigate("match") },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("game_over") {
            GameOverScreen(
                onTryAgainClicked = { navController.navigate("match") }
            )
        }
    }
}

Sau đó, bạn có thể gọi hàm này trong lambda mà bạn truyền đến NavHost thay vì gọi điều hướng cùng dòng. Sau đây là ví dụ minh hoạ:

@Composable
fun MyApp() {
    val navController = rememberNavController()
    NavHost(navController, startDestination = "title_screen") {
        composable("title_screen") {
            TitleScreen(
                onPlayClicked = { navController.navigate("register") },
                onLeaderboardsClicked = { /* Navigate to leaderboards */ }
            )
        }
        composable("register") {
            RegisterScreen(
                onSignUpComplete = { navController.navigate("gameInProgress") }
            )
        }

        // Add the nested graph using the extension function
        addNestedGraph(navController)
    }
}

XML

Khi dùng XML, bạn có thể dùng Trình chỉnh sửa điều hướng để tạo biểu đồ lồng nhau. Để thực hiện việc này, hãy làm theo các bước sau:

  1. Trong Navigation Editor (Trình chỉnh sửa điều hướng), nhấn và giữ phím Shift, sau đó nhấp vào các đích đến mà bạn muốn đưa vào biểu đồ lồng nhau đó.
  2. Nhấp chuột phải để mở trình đơn theo bối cảnh rồi chọn Move to Nested Graph (Di chuyển đến Biểu đồ lồng nhau) > New Graph (Biểu đồ mới). Các đích đến này sẽ được chứa trong một biểu đồ lồng nhau. Hình 2 cho thấy một biểu đồ lồng trong Navigation Editor (Trình chỉnh sửa điều hướng):

    Hình 2. Biểu đồ lồng nhau trong Trình chỉnh sửa điều hướng
  3. Nhấp vào biểu đồ lồng nhau. Các thuộc tính sau sẽ xuất hiện trong ngăn Attributes (Thuộc tính):

    • Type (Loại), chứa "Biểu đồ lồng nhau"
    • ID (Mã nhận dạng) chứa mã định dạng do hệ thống chỉ định cho biểu đồ lồng nhau. Mã nhận dạng này dùng để tham chiếu đến biểu đồ lồng nhau từ mã của bạn.
  4. Nhấp đúp vào biểu đồ lồng nhau để hiện ra các đích đến của biểu đồ này.

  5. Nhấp vào thẻ Text (Văn bản) để chuyển sang chế độ xem XML. Một biểu đồ điều hướng lồng nhau đã được thêm vào biểu đồ. Biểu đồ điều hướng này có các phần tử navigation riêng, cùng với mã nhận dạng riêng và thuộc tính startDestination trỏ đến đích đến đầu tiên trong biểu đồ lồng nhau:

    <?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"
        app:startDestination="@id/mainFragment">
        <fragment
            android:id="@+id/mainFragment"
            android:name="com.example.cashdog.cashdog.MainFragment"
            android:label="fragment_main"
            tools:layout="@layout/fragment_main" >
            <action
                android:id="@+id/action_mainFragment_to_sendMoneyGraph"
                app:destination="@id/sendMoneyGraph" />
            <action
                android:id="@+id/action_mainFragment_to_viewBalanceFragment"
                app:destination="@id/viewBalanceFragment" />
        </fragment>
        <fragment
            android:id="@+id/viewBalanceFragment"
            android:name="com.example.cashdog.cashdog.ViewBalanceFragment"
            android:label="fragment_view_balance"
            tools:layout="@layout/fragment_view_balance" />
        <navigation android:id="@+id/sendMoneyGraph" app:startDestination="@id/chooseRecipient">
            <fragment
                android:id="@+id/chooseRecipient"
                android:name="com.example.cashdog.cashdog.ChooseRecipient"
                android:label="fragment_choose_recipient"
                tools:layout="@layout/fragment_choose_recipient">
                <action
                    android:id="@+id/action_chooseRecipient_to_chooseAmountFragment"
                    app:destination="@id/chooseAmountFragment" />
            </fragment>
            <fragment
                android:id="@+id/chooseAmountFragment"
                android:name="com.example.cashdog.cashdog.ChooseAmountFragment"
                android:label="fragment_choose_amount"
                tools:layout="@layout/fragment_choose_amount" />
        </navigation>
    </navigation>
    
  6. Trong mã của bạn, hãy chuyển mã nhận dạng tài nguyên của thao tác kết nối biểu đồ gốc với biểu đồ lồng nhau:

    kotlin

    view.findNavController().navigate(R.id.action_mainFragment_to_sendMoneyGraph)
    

    java

    Navigation.findNavController(view).navigate(R.id.action_mainFragment_to_sendMoneyGraph);
    
  7. Quay lại thẻ Design (Thiết kế), quay lại biểu đồ gốc bằng cách nhấp vào Root (Gốc).

Tham chiếu các biểu đồ điều hướng khác bằng phần tử include

Một cách khác để mô-đun hoá cấu trúc biểu đồ của bạn là chèn một biểu đồ trong một biểu đồ khác thông qua phần tử <include> trong biểu đồ điều hướng chính. Điều này cho phép xác định biểu đồ đi kèm trong một mô-đun hoặc dự án riêng biệt nói chung, giúp tối đa hoá khả năng sử dụng lại.

Đoạn mã sau đây minh hoạ cách bạn có thể dùng <include>:

<!-- (root) nav_graph.xml -->
<?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/fragment">

    <strong><include app:graph="@navigation/included_graph" /></strong>

    <fragment
        android:id="@+id/fragment"
        android:name="com.example.myapplication.BlankFragment"
        android:label="Fragment in Root Graph"
        tools:layout="@layout/fragment_blank">
        <strong><action
            android:id="@+id/action_fragment_to_second_graph"
            app:destination="@id/second_graph" /></strong>
    </fragment>

    ...
</navigation>
<!-- included_graph.xml -->
<?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"
    <strong>android:id="@+id/second_graph"</strong>
    app:startDestination="@id/includedStart">

    <fragment
        android:id="@+id/includedStart"
        android:name="com.example.myapplication.IncludedStart"
        android:label="fragment_included_start"
        tools:layout="@layout/fragment_included_start" />
</navigation>

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.

Mẫu

Lớp học lập trình

Video