Navigation of Jetpack's Architecture Components

前言

Navigation 是应用程序设计的重要组成部分。 通过Navigation ,可以设计允许用户在应用内的不同内容区域中移动,移入和移出的交互。说白了其实是用来管理 APP 里页面跳转的。

使用

如果您想使用Android Studio导航,则必须使用Android Studio 3.3或更高版本。

要向项目添加导航图,请执行以下操作:

  1. 在“项目”窗口中,右键单击res目录,然后选择“ New > Android Resource File。 出现 New Resource File 对话框。

  2. 在“文件名”字段中键入名称,例如“nav_graph”。

  3. Resource type下拉列表中选择Navigation

  4. 单击确定。 发生以下情况:

    ​ 在res目录中创建navigation资源目录。
    ​ 在navigation目录中创建nav_graph.xml文件。
    ​ nav_graph.xml文件在导航编辑器中打开。 此XML文件包含导航图。

  5. 单击 Text选项卡以切换到XML文本视图。 您应该看到一个空的导航图,如以下示例所示:

    <?xml version="1.0" encoding="utf-8"?>
    <navigation xmlns:android="http://schemas.android.com/apk/res/android">
    </navigation>
    
  6. 单击Design 返回“导航编辑器”。

创建destinations

  1. 在导航栏编辑器中, 点击New Destination img, 然后点击 Create blank destination.
  2. New Android Component 弹窗出现, 输入一个Fragment Name.作为 Fragment类的名字
  3. 要让Android Studio为Fragment创建相应的布局资源文件,请选中Create layout XML旁边的框,然后在Fragment Layout Name字段中输入资源名称。
  4. Source Language 下拉框中为类源文件选择 Kotlin 或者 Java 语言。
  5. 点击Finish.

startDestination 代表开始节点

<?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/blankFragment">
    <fragment
        android:id="@+id/blankFragment"
        android:name="com.example.cashdog.cashdog.BlankFragment"
        android:label="fragment_blank"
        tools:layout="@layout/fragment_blank" />
    <fragment
        android:id="@+id/blankFragment2"
        android:name="com.example.cashdog.cashdog.BlankFragment2"
        android:label="Blank2"
        tools:layout="@layout/fragment_blank_fragment2" />
</navigation>

连接destinations

使用action连接两个destinations,如下所示:

在“设计”选项卡中,将鼠标悬停在您希望用户导航的目标的右侧。目的地上会出现一个圆圈。单击并按住,将光标拖到希望用户导航到的目标上,然后释放。绘制一条线以指示两个目的地之间的导航。相应的会生成xml代码

<?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/blankFragment">
    <fragment
        android:id="@+id/blankFragment"
        android:name="com.example.cashdog.cashdog.BlankFragment"
        android:label="fragment_blank"
        tools:layout="@layout/fragment_blank" >
        <action
            android:id="@+id/action_blankFragment_to_blankFragment2"
            app:destination="@id/blankFragment2" />
    </fragment>
    <fragment
        android:id="@+id/blankFragment2"
        android:name="com.example.cashdog.cashdog.BlankFragment2"
        android:label="fragment_blank_fragment2"
        tools:layout="@layout/fragment_blank_fragment2" />
</navigation>

在谷歌官方demo中,实现了一组fragment之间的关系,在action中可以设置转场动画

<?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"
            app:startDestination="@id/title_screen">
    <fragment
        android:id="@+id/title_screen"
        android:name="com.example.android.navigationsample.TitleScreen"
        android:label="fragment_title_screen"
        tools:layout="@layout/fragment_title_screen">
        <action
            android:id="@+id/action_title_screen_to_register"
            app:destination="@id/register"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />
        <action
            android:id="@+id/action_title_screen_to_leaderboard"
            app:destination="@id/leaderboard"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />
    </fragment>
    <fragment
        android:id="@+id/register"
        android:name="com.example.android.navigationsample.Register"
        android:label="fragment_register"
        tools:layout="@layout/fragment_register">
        <action
            android:id="@+id/action_register_to_match"
            app:destination="@id/match"
            app:enterAnim="@anim/slide_in_right"
            app:exitAnim="@anim/slide_out_left"
            app:popEnterAnim="@anim/slide_in_left"
            app:popExitAnim="@anim/slide_out_right" />
    </fragment>
    <fragment
            android:id="@+id/leaderboard"
            android:name="com.example.android.navigationsample.Leaderboard"
            android:label="fragment_leaderboard"
            tools:layout="@layout/fragment_leaderboard">
        <action
                android:id="@+id/action_leaderboard_to_userProfile"
                app:destination="@id/user_profile"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"/>
    </fragment>
    <fragment
            android:id="@+id/match"
            android:name="com.example.android.navigationsample.Match"
            android:label="fragment_match"
            tools:layout="@layout/fragment_match">
        <action
                android:id="@+id/action_match_to_in_game"
                app:destination="@id/in_game"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:enterAnim="@anim/slide_in_right"
                app:exitAnim="@anim/slide_out_left"/>
    </fragment>
    <fragment
            android:id="@+id/user_profile"
            android:name="com.example.android.navigationsample.UserProfile"
            android:label="fragment_user_profile"
            tools:layout="@layout/fragment_user_profile">
        <argument android:name="userName"
                  android:defaultValue="name"/>
        <deepLink app:uri="www.example.com/user/{userName}" />
    </fragment>
    <fragment
            android:id="@+id/in_game"
            android:name="com.example.android.navigationsample.InGame"
            android:label="Game"
            tools:layout="@layout/fragment_in_game">
        <action
                android:id="@+id/action_in_game_to_resultsWinner"
                app:destination="@id/results_winner"
                app:popUpTo="@+id/match"
                app:popUpToInclusive="false"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:enterAnim="@anim/fade_in"
                app:exitAnim="@anim/fade_out"/>
        <action
                android:id="@+id/action_in_game_to_gameOver"
                app:destination="@id/game_over"
                app:popUpTo="@id/match"
                app:popUpToInclusive="false"
                app:popEnterAnim="@anim/slide_in_left"
                app:popExitAnim="@anim/slide_out_right"
                app:enterAnim="@anim/fade_in"
                app:exitAnim="@anim/fade_out"/>
    </fragment>
    <fragment
            android:id="@+id/results_winner"
            android:name="com.example.android.navigationsample.ResultsWinner"
            tools:layout="@layout/fragment_results_winner">
        <action android:id="@+id/action_results_winner_to_leaderboard"
                app:destination="@id/leaderboard"
                app:popUpTo="@id/title_screen"/>
        <action android:id="@+id/action_results_winner_to_match"
                app:popUpTo="@id/match"/>
        <action
            android:id="@+id/action_results_winner_to_match2"
            app:destination="@id/match" />
    </fragment>
    <fragment
            android:id="@+id/game_over"
            android:name="com.example.android.navigationsample.GameOver"
            android:label="fragment_game_over"
            tools:layout="@layout/fragment_game_over">
        <action android:id="@+id/action_game_over_to_match"
                app:popUpTo="@id/match"/>
    </fragment>

</navigation>

修改Activity支持导航

activity为NavHost中的应用程序提供导航。 NavHost是一个当用户浏览您的应用程序时,目的地会被换入和换出空容器。

NavHost是一个接口,Navigation组件的默认NavHost实现是NavHostFragment。

xml实现

<FrameLayout
    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:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/background_light"
    tools:context="com.example.android.navigationsample.MainActivity">

    <fragment
            android:id="@+id/my_nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:defaultNavHost="true"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:navGraph="@navigation/navigation"/>
</FrameLayout>

navGraph 属性就是写刚才我们写的 nagation 文件

app:defaultNavHost =“true” 属性可确保NavHostFragment拦截系统“后退”按钮。 还可以通过覆盖AppCompatActivity.onSupportNavigateUp()并调用NavController.navigateUp来实现此行为,如下例所示:

@Override
public boolean onSupportNavigateUp() {
    return Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp();
}

当然也可以代码实现

NavHostFragment finalHost = NavHostFragment.create(R.navigation.example_graph);
getSupportFragmentManager().beginTransaction()
    .replace(R.id.nav_host, finalHost)
    .setPrimaryNavigationFragment(finalHost) // this is the equivalent to app:defaultNavHost="true"
    .commit();

界面跳转

使用NavController类导航到目标。 可以使用以下静态方法之一检索NavController

NavHostFragment.findNavController(Fragment)
Navigation.findNavController(Activity, @IdRes int viewId)
Navigation.findNavController(View)

拿到NavController后,使用其navigate()方法导航到目标。 navigate()方法接受资源ID。 ID可以是导航图的destination 或action中特定目标的ID。

  view.findViewById<Button>(R.id.play_btn).setOnClickListener {
            Navigation.findNavController(view).navigate(R.id.action_title_screen_to_register)
        }
        view.findViewById<Button>(R.id.leaderboard_btn).setOnClickListener {
            Navigation.findNavController(view).navigate(R.id.action_title_screen_to_leaderboard)
        }

------ 本文结束 ------
坚持原创技术分享,您的支持将鼓励我继续创作!