Các phương pháp hay nhất về tính năng điều hướng cho dự án có nhiều mô-đun

Một biểu đồ điều hướng có thể bao gồm bất kỳ tổ hợp nào sau đây:

  • Một đích duy nhất, chẳng hạn như đích <fragment>.
  • Một biểu đồ lồng ghép trong đó bao gồm một nhóm các đích có liên quan.
  • Một phần tử <include> cho phép bạn nhúng một tệp biểu đồ điều hướng khác như thẻ tệp này đã được lồng.

Tính linh hoạt này cho phép bạn kết hợp các biểu đồ điều hướng nhỏ hơn với nhau để tạo thành biểu đồ điều hướng hoàn chỉnh cho ứng dụng, ngay cả khi các biểu đồ điều hướng nhỏ hơn đó được cung cấp qua các mô-đun thư viện riêng biệt.

Đối với những ví dụ trong chủ đề này, mỗi mô-đun thư viện tập trung vào một tính năng và cung cấp một biểu đồ điều hướng bao gồm tất cả đích cần thiết để triển khai tính năng đó. Trong ứng dụng chính thức, bạn có thể có nhiều mô-đun con ở cấp thấp hơn. Đó là các mô-đun triển khai của mô-đun thư viện cấp cao. Mỗi mô-đun thư viện này đều được đưa vào, dù trực tiếp hay gián tiếp, trong mô-đun app của bạn. Ứng dụng nhiều mô-đun được lấy làm ví dụ trong tài liệu này có cấu trúc như sau:

cấu trúc ứng dụng cho ứng dụng ví dụ
đích bắt đầu của ứng dụng ví dụ
Hình 1. Cấu trúc ứng dụng và đích bắt đầu cho ứng dụng ví dụ.

Mỗi mô-đun thư viện là một đơn vị độc lập có biểu đồ điều hướng và đích riêng. Mô-đun app phụ thuộc vào từng mô-đun, và thêm các mục này dưới dạng chi tiết triển khai trong tệp build.gradle như sau:

dependencies {
    ...
    implementation project(":list")
    implementation project(":favorites")
    implementation project(":settings")

Vai trò của mô-đun app

Mô-đun app chịu trách nhiệm cung cấp biểu đồ hoàn chỉnh cho ứng dụng và thêm NavHost vào giao diện người dùng. Trong biểu đồ điều hướng của mô-đun app, bạn có thể tham chiếu đến các biểu đồ của thư viện bằng cách sử dụng <include>. Mặc dù cách sử dụng <include> cũng giống như hoạt động sử dụng biểu đồ lồng nhau, nhưng <include> hỗ trợ biểu đồ từ các mô-đun dự án khác hoặc từ dự án thư viện, như minh hoạ trong ví dụ sau:

<?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/list_nav_graph">

    <include app:graph="@navigation/list_navigation" />
    <include app:graph="@navigation/favorites_navigation" />
    <include app:graph="@navigation/settings_navigation" />
</navigation>

Sau khi một thư viện được đưa vào biểu đồ điều hướng ở cấp cao, bạn có thể di chuyển đến biểu đồ thư viện khi cần. Ví dụ: bạn có thể tạo một hành động để di chuyển đến biểu đồ cài đặt từ một mảnh trong biểu đồ điều hướng, như minh hoạ dưới đây:

<?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/list_nav_graph">

    <include app:graph="@navigation/list_navigation" />
    <include app:graph="@navigation/favorites_navigation" />
    <include app:graph="@navigation/settings_navigation" />

    <fragment
        android:id="@+id/random_fragment"
        android:name="com.example.android.RandomFragment"
        android:label="@string/fragment_random" >
        <!-- Launch into Settings Navigation Graph -->
        <action
            android:id="@+id/action_random_fragment_to_settings_nav_graph"
            app:destination="@id/settings_nav_graph" />
    </fragment>
</navigation>

Khi nhiều mô-đun thư viện cần tham chiếu đến một nhóm các đích phổ biến, chẳng hạn như một biểu đồ đăng nhập, bạn không nên đưa những đích phổ biến đó vào biểu đồ điều hướng của mỗi mô-đun thư viện. Thay vào đó, hãy thêm các đích phổ biến đó vào biểu đồ điều hướng của mô-đun app. Sau đó, mỗi mô-đun thư viện có thể chuyển đến các mô-đun thư viện để di chuyển đến các đích phổ biến đó.

Trong ví dụ trước, hành động chỉ định một đích đến điều hướng là @id/settings_nav_graph. Mã nhận dạng này đề cập đến một đích được xác định trong biểu đồ @navigation/settings_navigation. đi kèm

Thanh điều hướng cấp cao trong mô-đun ứng dụng

Thành phần Điều hướng bao gồm một lớp NavigationUI. Lớp này chứa các phương thức tĩnh quản lý hoạt động điều hướng bằng thanh ứng dụng trên cùng, ngăn điều hướng và trình đơn điều hướng ở dưới cùng. Nếu đích cấp cao của ứng dụng bao gồm các thành phần giao diện người dùng do các mô-đun thư viện cung cấp, thì mô-đun app sẽ là một vị trí tự nhiên để đặt các thành phần điều hướng và giao diện người dùng cấp cao. Vì mô-đun ứng dụng phụ thuộc vào các mô-đun thư viện cộng tác, nên bạn có thể truy cập vào tất cả đích của ứng dụng đó qua mã xác định trong mô-đun ứng dụng. Tức là bạn có thể sử dụng NavigationUI để gắn kết các đích với các mục trong trình đơn nếu mã của mục đó khớp với mã của đích.

Trong hình 2, mô-đun app ví dụ xác định BottomNavigationView trong hoạt động chính của mô-đun đó. Mã nhận dạng mục trình đơn trong trình đơn khớp với mã nhận dạng biểu đồ điều hướng của các biểu đồ thư viện:

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <item
        android:id="@id/list_nav_graph"
        android:icon="@drawable/ic_list"
        android:title="List"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@id/favorites_nav_graph"
        android:icon="@drawable/ic_favorite"
        android:title="Favorites"
        app:showAsAction="ifRoom"/>

    <item
        android:id="@id/settings_nav_graph"
        android:icon="@drawable/ic_settings"
        android:title="Settings"
        app:showAsAction="ifRoom" />
</menu>

Để cho phép NavigationUI xử lý thao tác ở dưới cùng, hãy gọi setupWithNavController() qua onCreate() trong lớp hoạt động chính của bạn, như trong ví dụ sau:

Kotlin

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)
    val navHostFragment =
        supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
    val navController = navHostFragment.navController

    findViewById<BottomNavigationView>(R.id.bottom_nav)
            .setupWithNavController(navController)
}

Java

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    NavHostFragment navHostFragment =
            (NavHostFragment) supportFragmentManager.findFragmentById(R.id.nav_host_fragment);
    NavController navController = navHostFragment.getNavController();
    BottomNavigationView bottomNav = findViewById(R.id.bottom_nav);

    NavigationUI.setupWithNavController(bottomNav, navController);
}

Với mã này, NavigationUI sẽ di chuyển đến biểu đồ thư viện thích hợp khi người dùng nhấp vào một mục điều hướng ở phía dưới cùng.

Xin lưu ý rằng nói chung mô-đun ứng dụng không nên có phần phụ thuộc cứng vào một đích cụ thể được nhúng sâu trong biểu đồ điều hướng của các mô-đun thư viện. Trong hầu hết các trường hợp, mô-đun ứng dụng chỉ nên biết về điểm truy cập đến biểu đồ điều hướng được nhúng hoặc được bao gồm (điều này cũng áp dụng bên ngoài các mô-đun thư viện). Nếu cần liên kết đến một đích sâu trong biểu đồ điều hướng của thư viện, bạn nên sử dụng đường liên kết sâu. Liên kết sâu cũng là cách duy nhất để thư viện chuyển đến đích trong biểu đồ điều hướng của thư viện khác.

Điều hướng giữa các mô-đun thư viện

Tại thời điểm biên dịch, các mô-đun thư viện độc lập không thể thấy nhau, vì vậy, bạn không thể sử dụng mã nhận dạng để điều hướng đến đích trong các mô-đun khác. Thay vào đó, hãy sử dụng đường liên kết sâu để đi theo chỉ dẫn đến một đích được liên kết với một đường liên kết sâu ngầm ẩn.

Tiếp tục ví dụ trước, hãy tưởng tượng bạn cần chuyển từ một nút trong mô-đun danh sách đến đích lồng trong mô-đun cài đặt. Bạn có thể thực hiện việc này bằng cách thêm một đường liên kết sâu đến đích trong biểu đồ điều hướng cài đặt, minh hoạ như sau:

<?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/settings_nav_graph"
    app:startDestination="@id/settings_fragment_one">

    ...

    <fragment
        android:id="@+id/settings_fragment_two"
        android:name="com.example.google.login.SettingsFragmentTwo"
        android:label="@string/settings_fragment_two" >

        <deepLink
            app:uri="android-app://example.google.app/settings_fragment_two" />
    </fragment>
</navigation>

Sau đó, hãy thêm mã sau vào onClickListener của nút trong mảnh trong danh sách:

Kotlin

button.setOnClickListener {
    val request = NavDeepLinkRequest.Builder
        .fromUri("android-app://example.google.app/settings_fragment_two".toUri())
        .build()
    findNavController().navigate(request)
}

Java

button.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        NavDeepLinkRequest request = NavDeepLinkRequest.Builder
            .fromUri(Uri.parse("android-app://example.google.app/settings_fragment_two"))
            .build();
        NavHostFragment.findNavController(this).navigate(request);
    }
});

Không giống như điều hướng bằng cách sử dụng mã nhận dạng hành động hoặc mã nhận dạng đích, bạn có thể điều hướng đến URI bất kỳ trong biểu đồ bất kỳ, ngay cả trên các mô-đun.

Khi điều hướng bằng URI, ngăn xếp lui không được đặt lại. Hành vi này không giống như cách điều hướng sử dụng liên kết sâu rõ ràng, trong đó ngăn xếp lui được thay thế khi di chuyển.