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 biểu đồ điều hướng nhỏ hơn được cung cấp qua các mô-đun riêng biệt.

Đối với các ví dụ trong chủ đề này, mỗi mô-đun tính năng tập trung vào một tính năng và cung cấp một biểu đồ điều hướng đóng gói mọi đích đến cần để 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 tính năng cấp cao. Mỗi mô-đun tính năng 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:

biểu đồ phần phụ thuộc của một ứng dụng nhiều mô-đun mẫu
đích bắt đầu của ứng dụng ví dụ
Hình 1. Cấu trúc ứng dụng và đích đến bắt đầu cho ứng dụng ví dụ.

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

Groovy

dependencies {
    ...
    implementation project(":feature:home")
    implementation project(":feature:favorites")
    implementation project(":feature:settings")

Kotlin

dependencies {
    ...
    implementation(project(":feature:home"))
    implementation(project(":feature:favorites"))
    implementation(project(":feature: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 đồ thư viện bằng cách sử dụng <include>. Tuy cách sử dụng <include> cũng giống như cách sử dụng biểu đồ lồng nhau, nhưng <include> có hỗ trợ biểu đồ của các mô-đun dự án khác hoặc của dự án thư viện, như minh hoạ trong ví dụ sau đâ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/home_nav_graph">

    <include app:graph="@navigation/home_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ể điều hướng đến biểu đồ thư viện khi cần. Ví dụ: bạn có thể tạo một hành động để điều hướng đến biểu đồ cài đặt qua 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/home_nav_graph">

    <include app:graph="@navigation/home_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 tính năng cần tham chiếu đến một nhóm đích đến 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 đến phổ biến đó vào biểu đồ điều hướng của từng mô-đun tính năng. 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 tính năng có thể điều hướng giữa các mô-đun tính năng để đế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ý cách đ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 tính năng cung cấp, thì mô-đun app là một nơi 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 tính năng cộng tác, nên bạn có thể truy cập mọi đích của ứng dụng đó qua mã được xác định trong mô-đun ứng dụng. Tức là bạn có thể sử dụng NavigationUI để gắn các đích đến với các mục trong trình đơn nếu mã nhận dạng của mục đó khớp với mã nhận dạng của đích đến.

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/home_nav_graph"
        android:icon="@drawable/ic_home"
        android:title="Home"
        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 di chuyển đến biểu đồ thư viện thích hợp khi người dùng nhấp vào mục điều hướng ở phía dưới cùng.

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

Điều hướng giữa các mô-đun tính năng

Các mô-đun tính năng độc lập không thể nhìn thấy nhau tại thời điểm biên dịch, vậy nên bạn không thể dùng mã nhận dạng để di chuyển đến đích đến trong các mô-đun khác. Thay vào đó, hãy sử dụng đường liên kết sâu để trực tiếp di chuyển đến một đích đến đượ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, giả sử bạn cần điều hướng từ một nút trong mô-đun :feature:home đến đích được lồng trong mô-đun :feature:settings. 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 vào đích đến trong biểu đồ điều hướng cài đặt, 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/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 đây vào onClickListener của nút trong mảnh trang 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 đường liên kết sâu tường minh, trong đó ngăn xếp lui được thay thế khi điều hướng.