设计导航图

Navigation 组件使用导航图管理应用导航。导航图是一种资源文件,其中包含您应用的所有目的地和逻辑连接(后者也称为“操作”,用户可以执行以从一个目的地导航到另一个目的地)。您可以使用 Android Studio 中的 Navigation Editor 管理应用的导航图。

本主题包含了设计应用导航图的最佳做法和建议。

顶级导航图

应用的顶级导航图应从用户启动您的应用时看到的初始目的地开始,且应包含他们在应用中四处移动时看到的目的地。

图 1:顶级导航图。

嵌套图表

应用中的登录流程、向导或其他子流程通常是嵌套导航图的最佳表示形式。通过以这种方式嵌套独立的子导航流程,您可以更轻松地理解和管理应用界面的主要流程。此外,嵌套图表可以重复使用。嵌套图表还提供一定程度的封装,即嵌套图表之外的目的地无法直接访问嵌套图表内的任何目的地。相反,这些目的地应 navigate() 到嵌套图表本身,在嵌套图表中,内部逻辑可以发生更改,而不会影响图表的其余部分。

以图 1 中的顶级导航图为例,假设您希望仅在用户首次启动应用时让其看到 title_screen 和 register 屏幕。之后,系统会存储用户信息。用户以后启动应用时,您应直接将其转到匹配屏幕。最佳做法是,您将匹配屏幕设置为顶级导航图的起始目的地,并将标题屏幕和注册屏幕移至嵌套图表,如图 2 所示:

图 2:顶级导航图现在包含一个嵌套图表。

匹配屏幕启动后,您可以检查是否有注册用户。如果用户未注册,您可以将其转到注册屏幕。如需详细了解条件导航情形,请参阅条件导航

将图表结构模块化的另一种方法是,通过父级导航图中的 <include> 元素,将一个图表包含在另一个图表中这样一来,将可以完全在单独的模块或项目中定义包含的图表,从而最大限度提高可重用性。

如果您的应用依赖包含导航图的库模块,则您可以使用 <include> 元素引用这些导航图。

就我们的知识问答游戏示例而言,假设您想要将应用的以游戏为中心的部分分隔到一个单独的库模块中,以便在多个应用中包含 in_game、results_winner 和 game_over 屏幕,如以下示例所示:

<!-- App Module Navigation Graph -->
    <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"
               app:startDestination="@id/match">

       <fragment android:id="@+id/match"
               android:name="com.example.android.navigationsample.Match"
               android:label="fragment_match">

           <!-- Launch into In Game Modules Navigation Graph -->
           <action android:id="@+id/action_match_to_in_game_nav_graph"
               app:destination="@id/in_game_nav_graph" />
       </fragment>

       <include app:graph="@navigation/in_game_navigation" />

    </navigation>
    
<!-- Game Module Navigation Graph -->
    <?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"
       android:id="@+id/in_game_nav_graph"
       app:startDestination="@id/in_game">

       <fragment
           android:id="@+id/in_game"
           android:name="com.example.android.gamemodule.InGame"
           android:label="Game">
           <action
               android:id="@+id/action_in_game_to_resultsWinner"
               app:destination="@id/results_winner"  />
           <action
               android:id="@+id/action_in_game_to_gameOver"
               app:destination="@id/game_over"  />
       </fragment>

       <fragment
           android:id="@+id/results_winner"
           android:name="com.example.android.gamemodule.ResultsWinner" >

           <!-- Action back to destination which launched into this in_game_nav_graph-->
           <action android:id="@+id/action_pop_out_of_game"
                               app:popUpTo="@id/in_game_nav_graph" />

       </fragment>

       <fragment
           android:id="@+id/game_over"
           android:name="com.example.android.gamemodule.GameOver"
           android:label="fragment_game_over"
           tools:layout="@layout/fragment_game_over" >

          <!-- Action back to destination which launched into this in_game_nav_graph-->
           <action android:id="@+id/action_pop_out_of_game"
                               app:popUpTo="@id/in_game_nav_graph" />

     </fragment>

    </navigation>
    

您应该在主应用模块中纳入顶级图表,并且对于包含您需要引用的导航的任何模块,都拥有 Gradle 引用。

全局操作

对于应用中的任何可通过多条路径到达的目的地,您都应定义可转到它的相应全局操作。全局操作可用于从任意位置导航到某目的地。

下面我们将全局操作应用于库模块示例,该示例在获胜目的地和游戏结束目的地中定义了同一操作。您应将这些常见操作提取到一个全局操作,并从这两个目的地引用这些操作,如以下示例所示:

<?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"
       android:id="@+id/in_game_nav_graph"
       app:startDestination="@id/in_game">

       <!-- Action back to destination which launched into this in_game_nav_graph-->
       <action android:id="@+id/action_pop_out_of_game"
                           app:popUpTo="@id/in_game_nav_graph"
                           app:popUpToInclusive="true"  />

       <fragment
           android:id="@+id/in_game"
           android:name="com.example.android.gamemodule.InGame"
           android:label="Game">
           <action
               android:id="@+id/action_in_game_to_resultsWinner"
               app:destination="@id/results_winner"  />
           <action
               android:id="@+id/action_in_game_to_gameOver"
               app:destination="@id/game_over"  />
       </fragment>

       <fragment
           android:id="@+id/results_winner"
           android:name="com.example.android.gamemodule.ResultsWinner" />

       <fragment
           android:id="@+id/game_over"
           android:name="com.example.android.gamemodule.GameOver"
           android:label="fragment_game_over"
           tools:layout="@layout/fragment_game_over" />

    </navigation>
    

如需了解详情以及查看如何在 Fragment 中使用全局操作的示例,请参阅导航文档中的全局操作