Android Jetpack Navigation 导航组件的使用

Android Jetpack:Navigation 导航组件的使用

官方介绍

导航组件是一套用于应用内导航的库,工具和指南。该组件将应用程序的所有导航信息集中在导航图中,为实现从简单按钮点击到复杂导航UI模式的所有操作提供了强大的框架。

Android Studio 3.3包含导航编辑器,可以显示导航图。其他功能和优点包括:

  • 自动处理片段交互
  • 正确处理上方和后方
  • 动画和过渡的默认行为
  • 深度链接,包括正确的反向堆栈生成
  • 只需少许功夫,就可以使用导航UI库实现常见模式,如导航抽屉和底部导航→http://bit.ly/2EWYtoV
  • 在使用Safe Args插件导航和传递信息的同时,保证输入安全性→http://bit.ly/2VR7kPM

要了解有关导航的更多信息,请查看:

  • 导航组件文档→http://bit.ly/2TJuhao
  • 导航Codelab→http://bit.ly/2TSa9mB
  • Single Activity:在Android 2018开发者峰会中的“为什么”,“何时”和“如何”→http://bit.ly/2u5OoRC
  • 安卓工作室 3.3 博文→http://bit.ly/2VWxJMc
  • 基础导航参考文档→http://bit.ly/2EY0Mbn
  • 片段导航参考文档→http://bit.ly/2UB0ltY
  • 导航UI参考文档→http://bit.ly/2FbXKBL
  • 源代码(AOSP的一部分)→http://bit.ly/2UBbHOG
  • 导航编辑器问题跟踪器→http://bit.ly/2VWvrws
  • 导航组件问题跟踪器(非导航编辑器)→http://bit.ly/2CjXazR

准备工作

下载代码:

$ git clone https://github.com/googlecodelabs/android-navigation

安装Android Studio 3.2或者更高版本

导航简述:

  • 导航图:新类型的XML资源文件,包含所有导航相关的信息。
  • NavHostFragment:添加到XML布局文件中的类,用于展示导航图中的各个目的地。
  • NavController:导航控制器,代码中通过它实现导航操作与控制。

导航图

Android Studio 3.2之后的版本支持导航编辑器,需要先升级到此版本之上。

导航图是一种新的资源文件,可以在其中定义应用中所有可能的路径,并且以可视化的方式展现。![2e3dff480efde8d5](/Users/journey/OneDrive - Premium/我的文章/assets/2e3dff480efde8d5.png)

使用导航图进行导航

首先你需要在activity layouts中添加一个组件 NavHostFragment

<LinearLayout
    .../>
    <androidx.appcompat.widget.Toolbar
        .../>
    <fragment
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:id="@+id/my_nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:navGraph="@navigation/mobile_navigation"
        app:defaultNavHost="true"
        />
    <com.google.android.material.bottomnavigation.BottomNavigationView
        .../>
LinearLayout>

注意:

  • android:name="androidx.navigation.fragment.NavHostFragment" andapp:defaultNavHost="true" 会将系统的返回键关联到 NavHostFragment
  • app:navGraph="@navigation/mobile_navigation" 声明了 NavHostFragment的导航图

NavHostFragment中使用一个特殊的类NavController作为导航控制器。

// Command to navigate to flow_step_one_dest
findNavController().navigate(R.id.flow_step_one_dest)

你可以传入destination ID或者action ID作为参数,这些ID必须在导航图XML文件中注册。

可以用以下三种方法获得NavController

  • Fragment.findNavController()
  • View.findNavController()
  • Activity.findNavController(viewId: Int)

不管使用哪种方法,必须确保fragment, view, view ID是NavHostFragment类型,或者是其子类。否则会出现IllegalStateException

添加过渡动画

通过重写NavOptions可以改变导航默认的过渡效果。

val options = navOptions {
    anim {
        enter = R.anim.slide_in_right
        exit = R.anim.slide_out_left
        popEnter = R.anim.slide_in_left
        popExit = R.anim.slide_out_right
    }
}
view.findViewById<Button>(R.id.navigate_destination_button)?.setOnClickListener {
    findNavController().navigate(R.id.flow_step_one_dest, null, options)
}

使用Action进行导航

在导航图中,使用带箭头的线条表示action

![82f00bfa471bd0ff](/Users/journey/OneDrive - Premium/我的文章/assets/82f00bfa471bd0ff.png)

使用action进行导航有以下优点:

  • 导航路径可视化
  • 可以包含额外的属性设置,例如过渡动画,传递参数,后退行为等
  • 可以使用safe args插件进行导航
<fragment
    android:id="@+id/flow_step_two_dest"
    android:name="com.example.android.codelabs.navigation.FlowStepFragment">

    <argument
        .../>

    <action
        android:id="@+id/next_action"
        app:destination="@+id/flow_step_one"
        app:enterAnim="@anim/slide_in_right"
        app:exitAnim="@anim/slide_out_left"
        app:popEnterAnim="@anim/slide_in_left"
        app:popExitAnim="@anim/slide_out_right"
        app:popUpTo="@id/home_dest">
    action>
fragment>

popUpTo属性用于制定栈底的导航。

在action标签允许你在XML文件中设置好NavOptions中的选项。

Safe Args

导航组件有个Gradle插件叫做safe args,可以生成类型安全的简单类型,用作destination和action的传递参数。

safe args可以帮你取代以下代码:

val username = arguments?.getString("usernameKey")

替换为这种写法:

val username = args.username

使用safe args

  1. 打开项目的build.gradle

    dependencies {
            classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
        //...
        }
    
  2. 打开 app/build.gradle

    apply plugin: 'com.android.application'
    apply plugin: 'kotlin-android'
    apply plugin: 'androidx.navigation.safeargs'
    
    android { 
       //...
    }
    
  3. 在导航图中添加argument

    <fragment
        android:id="@+id/flow_step_one_dest"
        android:name="com.example.android.codelabs.navigation.FlowStepFragment"
        tools:layout="@layout/flow_step_one_fragment">
        <argument
            android:name="flowStepNumber"
            app:argType="integer"
            android:defaultValue="1"/>
    
        <action...>
        action>
    fragment>
    
  4. 在代码中获取参数

    val safeArgs: FlowStepFragmentArgs by navArgs()
    val flowStepNumber = safeArgs.flowStepNumber
    

使用菜单,抽屉,底部按钮进行导航

导航组件中包含 NavigationUI 类, navigation-ui-ktx kotlin拓展。NavigationUI中有许多提供给菜单导航所需的静态方法,如果NavigationUI在菜单选项中找到了和导航图中相同的destination ID,它会将其导航到所配置的目的地。

选项菜单导航

使用NavigationUI最简单的方法之一就是用它来进行option menu的配置。NavigationUI能使onOptionsItemSelected callback变得更加简单。

overflow_menu.xml

<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:menuCategory="secondary"
    android:title="@string/settings" />
    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
                || super.onOptionsItemSelected(item)
    }

底部导航配置

在底部导航中配置与导航图中相同的destination ID。

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@id/home_dest"
        android:icon="@drawable/ic_home"
        android:title="@string/home" />
    <item
        android:id="@id/deeplink_dest"
        android:icon="@drawable/ic_android"
        android:title="@string/deeplink" />
menu>

使用setupWithNavController(bottomNavigationView: BottomNavigationView, navController: NavController)实现 setupBottomNavMenu 方法 。

private fun setupBottomNavMenu(navController: NavController) {
    val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_nav_view)
    bottomNav?.setupWithNavController(navController)
}

抽屉导航配置

在目标Activity中,实现setupNavigationMenu

private fun setupNavigationMenu(navController: NavController) {
    val sideNavView = findViewById<NavigationView>(R.id.nav_view)
    sideNavView?.setupWithNavController(navController)
}

通过AppBarConfiguration设置 ActionBar 。通过AppBarConfiguration 配置好应用的顶层destination IDs 与drawer layout。

val drawerLayout : DrawerLayout? = findViewById(R.id.drawer_layout)
appBarConfiguration = AppBarConfiguration(
        setOf(R.id.home_dest, R.id.deeplink_dest),
        drawerLayout)

配置好AppBarConfiguration之后,你可以调用 NavigationUI.setupActionBarWithNavController,它帮你做了以下事情:

  • 在ActionBar显示title为当前目的地的label
  • 在次级页面显示返回箭头
  • 在顶级页面显示汉堡按钮
override fun onSupportNavigateUp(): Boolean {
    return findNavController(R.id.my_nav_host_fragment).navigateUp(appBarConfiguration)
}

根据Material Design guidelines,你不应该同时使用抽屉导航与底部导航。如果你在应用的竖屏模式布局下使用了底部导航,建议在分屏模式下使用抽屉导航。这里的分屏模式布局文件为navigation_activity.xml (w960dp)

添加新的导航到NavigationView也非常简单,你只需要在menu的XML文件中新增一个item就可以了。

<item
    android:id="@+id/settings_dest"
    android:icon="@drawable/ic_settings"
    android:title="@string/settings" />

添加Deep Link支持

使用导航库来处理Deep Link的好处之一就是,跳转到正确的页面之后,导航库会帮你找到合适的后退栈。

添加Deep Link

我们使用 NavDeepLinkBuilder 来连接微件与目的地。

val args = Bundle()
args.putString("myarg", "From Widget");
val pendingIntent = NavDeepLinkBuilder(context)
        .setGraph(R.navigation.mobile_navigation)
        .setDestination(R.id.deeplink_dest)
        .setArguments(args)
        .createPendingIntent()

remoteViews.setOnClickPendingIntent(R.id.deep_link_button, pendingIntent)

Deep Link后退栈

后退栈取决于你导航图中的设置。

后退栈由destinations中声明了 app:startDestination的属性决定。

在更富在的导航图中, app:startDestination会决定各个层级的后退栈。

更多内容查看 Principles of Navigation。

关联网页链接

传统方式是 intent-filter and associate a URL with the activity,不过导航库会让这一步骤更加简单。

URI匹配支持以下特性:

  • 域名默认http,https协议
  • 可以使用占位符{placeholder_name}来匹配更多字符。占位符名称需要与入参相同。 http://www.example.com/users/{id} will match http://www.example.com/users/4
  • 可以使用.*
  • NavController会自动处理 ACTION_VIEW intents
  1. 在导航图中,添加

    <fragment
        android:id="@+id/deeplink_dest"
        android:name="com.example.android.codelabs.navigation.DeepLinkFragment"
        android:label="@string/deeplink"
        tools:layout="@layout/deeplink_fragment">
    
        <argument
            android:name="myarg"
            android:defaultValue="Android!"/>
    
        <deepLink app:uri="www.example.com/{myarg}" />
    fragment>
    
  2. 修改AndroidManifest

    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.DEFAULT" />
            <category android:name="android.intent.category.LAUNCHER" />
        intent-filter>
    
        <nav-graph android:value="@navigation/mobile_navigation" />
    activity>
    

生成后的AndroidManifest其实是这样的:

<activity
    android:name="com.example.android.codelabs.navigation.MainActivity">
    <intent-filter>
        ...
    intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <category android:name="android.intent.category.BROWSABLE" />
        <data android:scheme="http" />
        <data android:scheme="https" />
        <data android:host="www.example.com" />
        <data android:pathPrefix="/" />
    intent-filter>
activity>

你可能感兴趣的:(移动开发)