Android导航组件学习(一)

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第6天,点击查看活动详情

添加依赖

使用导航组件之前需要添加相关的依赖,在本篇笔记中使用的代码都是用的kotlin,所以这里只添加的kotlin相关的依赖,如下所示:

def nav_version = "2.4.2"
  implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
  implementation "androidx.navigation:navigation-ui-ktx:$nav_version"
复制代码

如果需要添加其它语言的支持依赖,可以访问这里的地址添加对应的依赖。

导航图

之前我们进行Fragment切换,基本上都是使用FragmentManager配合FragmentTranscation自己进行操作的,但是使用导航组件则不是这样,我们需要创建导航图来指定页面(Activity或者Fragment)之间的跳转。

创建导航图

导航图是一种资源文件,其中包含了所有目的地和操作,这个图标会显示应用所有的导航路径,下面是一个简单的导航图的示例. 一个简单地导航图示例

首次添加导航图资源的时候需要执行如下操作:

  • 需要创建一个导航图资源,如nav_test.xml,这里就和普通创建资源文件的方式是一样的,需要注意的就是在最后选择类型的时候选择Navigation,这之后就会在当前项目的res目录下添加一个navigation目录,此目录下就有刚才创建的nav_test.xml这个导航图资源文件

导航图文件说明

从文件的命名上我们就可以知道,这是一个xml文件,创建了nav_test.xml资源文件时,AS会自动帮我们打开Navigation Editor,我们可以在这里直观地对导航图进行修改,也可以直接修改其中的xml代码。

添加导航宿主

导航宿主是Navigation组件的核心部分之一,这是一个空的容器,用于在应用中导航时,目的地会在该容器中交换进出。

导航宿主必须实现NavHost接口,导航组件的默认NavHost实现NavHostFragment负责处理Fragment的目的地交换。

另外需要注意的是:导航组件旨在用于具有一个Activity和多个Fragment目的地的应用。主Activity与导航图相关联。且包含一个负责根据需要交换目的地的NavHostFragment。在具有多个Activity的应用中,每个Activity均具有自己的导航图。

下面是在我们的代码中的一个Activity中为了使用导航组件添加的一个Fragment:

        <!--导航容器-->
        <androidx.fragment.app.FragmentContainerView
                android:id="@+id/nav_fragment"
                android:name="androidx.navigation.fragment.NavHostFragment"
                android:layout_width="@dimen/dp_0"
                android:layout_height="@dimen/dp_0"
                app:layout_constraintTop_toBottomOf="@id/title"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintBottom_toBottomOf="parent"
                app:defaultNavHost="true"
                app:navGraph="@navigation/nav_test"
                />
复制代码

其实导航容器就是为了处理这个页面的不同的Frament而使用的,这里默认就使用了NavHostFragment,它可以提供不同Fragment目的地的跳转和交互。

需要注意以下内容:

  • name属性指定了我们使用的Fragment,这里就使用默认的NavHostFragment
  • navGraph指定了当前Activity使用的导航图
  • defaultNavHost表示NavHostFragment可以拦截系统返回键。

添加目的地

在上面我们添加了导航容器,下面就可以添加导航目的地了,导航目的地在导航容器中交换和跳转,我们可以首先创建一个Fragment作为导航目的地。Fragment非常简单,可以自行创建。

创建完Fragment之后,我们就可以回到之前创建的nav_test.xml文件中,在code视图下通过点击New Destination来创建一个目的地,在创建的时候我们就可以选择刚才已经创建好的Fragment作为目的地,添加完目的地后,就可以在资源文件中看到这个设置了:

    <fragment
            android:id="@+id/simpleContentFragment"
            android:name="yourPackageName.SimpleContentFragment"
            android:label="@string/simple_content_fragment" />
复制代码

上面的字段解释如下:

  • fragment: 这个表示的是目的的类型,表示当前的目的地是ActivityFragment或者其它的自定义类型
  • label: 这个字段用于设置目的地的用户可读名称
  • name: 这个字段表示的是与该目的地相关联的类的名称
  • id: 表示该目的地的id,它用于在代码中引用该目的地

执行完上面的操作之后,运行代码,就可以在页面中看到我们添加的那个Fragment了,也就是上面的SimpleContentFragment,运行结果如下图所示:

显示指定的Fragment

可以看到,这里显示的就是我们定义的SimpleContentFragment,也就是说,我们定义的导航组件是有效果的,我们在FragmentContainerView中指定的是NavHostFragment,显示的是我们在nav_test.xml文件中定义的SimpleContentFragment

连接到目的地

对于单ActivityFragment的应用程序来说,Fragment才是页面的真正的载体,也就必然存在从一个Fragment跳转到另一个Fragment的操作,在使用导航组件的时候,我们有两种方式实现这个操作:

  • 使用action标签从一个目的地连接到另一个目的地
  • 创建全局操作,从应用的任意位置跳转到特定的目的地

使用action标签可以表示从一个目的地连接到另一个目的地的操作,但是我们仍然需要编写执行导航操作的代码,加入我们需要从一个Fragment跳转到另一个Fragment中,我们可以在nav_test.xml文件中添加如下的代码:

    <fragment
            android:id="@+id/simpleContentFragment"
            android:name="yourpackagename.SimpleContentFragment"
            android:label="@string/simple_content_fragment"
            tools:layout="@layout/fragment_simple_content">
        <action
                android:id="@+id/action_to_nav_getting_start"
                app:destination="@id/navGettingStartFragment" />

    </fragment>

    <fragment
            android:id="@+id/navGettingStartFragment"
            android:name="yourpackagename.NavGettingStartFragment"
            android:label="@string/nav_fragment_getting_start"
            tools:layout="@layout/fragment_nav_getting_start" />
复制代码

从上面的文件中就可以看到,我们又定义了一个NavGettingStartFragment,同时定义了一个action操作,这个action表示了我们将会从SimpleContentFragment连接到NavGettingStartFragment中,其中id表示的是自身的id,destination表示的是要连接到的目的地的id,action操作至少应该包含这两个id。

导航到目的地

上面我们已经说过,nav_test.xml中通过action只能指定连接操作,但是仍然是需要我们添加跳转功能的代码.这里的跳转跳转操作是通过NavController完成的,它是一个在NavHost中管理应用导航的对象。每个NavHost均有自己的NavController,不同的NavHost中通过下面的方式检索NavController

  • Fragment中使用Fragment.findNavController()
  • View中使用View.findNavController()
  • Activity中使用Activity.findNavController(viewId: Int)

加入我们希望从SimpleContentFragment导航到NavGettingStartFragment中,我们便可以做如下的操作:

    findNavController(R.id.nav_fragment).navigate(R.id.action_to_nav_getting_start)
复制代码

Activity中通过finNavController(viewId: Int)来查找对应的NavController,之后调用它的navigate(id)方法即可执行跳转。

导航到目的地

但是需要注意的是:如果是使用FragmentContainerView创建的NavHostFragment,或者通过FragmentTransaction手动将NavHostFragment添加到Activity中时,此时在ActivityonCreate()方法中使用findNavController(R.id.nav_fragment)检索NavController将会失败并抛出异常,这个时候应该改为直接从NavHostFragment中直接检索NavController,如下所示:

//直接检索NavController抛出异常
     Caused by: java.lang.IllegalStateException: Activity XXXActivity@a3b4b50 does not have a NavController set on 2131296826
        at androidx.navigation.Navigation.findNavController(Navigation.kt:50)
        at androidx.navigation.ActivityKt.findNavController(Activity.kt:31)
复制代码

在如上的情况下,如果需要在ActivityonCreate()阶段就使用NavController则应使用如下的方式:

        val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_fragment) as NavHostFragment
        navHostFragment.navController.navigate(R.id.action_to_nav_getting_start)
复制代码

还有一点需要注意的是,如果当前已经处于要导航的目的地了,那么继续使用同样的actionId进行导航则会报错,如下所示:

//在已经进行过一次导航的基础上继续使用同一个actionId继续进行导航出现如下的错误:
Caused by: java.lang.IllegalArgumentException: Navigation action/destination xxx:id/action_to_nav_getting_start cannot be found from the current destination Destination(xxx:id/navGettingStartFragment) label=nav_getting_start_fragment class=xxx.NavGettingStartFragment
        at androidx.navigation.NavController.navigate(NavController.kt:1536)
        at androidx.navigation.NavController.navigate(NavController.kt:1468)
复制代码

猜你喜欢

转载自juejin.im/post/7106044886515712007