欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Navigation使用指南

程序员文章站 2022-03-03 19:55:19
概述Navigation跳转1.环境配置在app model下配置://build.gradleimplementation deps.navigation.fragment_ktximplementation deps.navigation.ui_ktx相关配置://versions.gradleversions.navigation = "2.1.0"def navigation = [:]navigation.fragment_ktx = "androidx.navigatio...

Navigation跳转

1.环境配置

在app model下配置:

//build.gradle
implementation deps.navigation.fragment_ktx
implementation deps.navigation.ui_ktx

相关配置:

//versions.gradle
versions.navigation = "2.1.0"
def navigation = [:]
navigation.fragment_ktx = "androidx.navigation:navigation-fragment-ktx:$versions.navigation"
navigation.ui_ktx = "androidx.navigation:navigation-ui-ktx:$versions.navigation"
navigation.safe_args = "androidx.navigation:navigation-safe-args-gradle-plugin:$versions.navigation"
deps.navigation = navigation

2.NavHostFragment配置

Google提供的Navigation的使用模式是:Activity中包含一个宿主NavHostFragment,该fragment用来解析跳转树NavGragh,并在初始化完成后被替换为跳转树中startDestination指向的节点。下面看一下NavHostFragment怎么样被引入到Activity的布局文件的:

//activity_navigation.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
    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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary"
            android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
            app:layout_constraintTop_toTopOf="parent"/>

        <fragment
            android:id="@+id/my_nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintTop_toBottomOf="@id/toolbar"
            app:layout_constraintBottom_toBottomOf="parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/mobile_navigation" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.drawerlayout.widget.DrawerLayout>

主要是用<fragment>标签引入,name指向该NavHostFragment,app:navGraph指向跳转树的资源文件定义:

<?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/mobile_navigation"
    app:startDestination="@id/navHomeFragment">

    <fragment
        android:id="@+id/navHomeFragment"
        android:name="com.yy.onepiece.jetpackpractice.ui.NavHomeFragment"
        android:label="NavHomeFragment"
        tools:layout="@layout/fragment_nav_home">
        <action
            android:id="@+id/action_navHomeFragment_to_navStepFragment"
            app:destination="@id/navStepFragmentOne" />
    </fragment>
    <fragment
        android:id="@+id/navStepFragmentOne"
        android:name="com.yy.onepiece.jetpackpractice.ui.NavStepFragment"
        android:label="NavStepFragment"
        tools:layout="@layout/fragment_nav_step_one">
        <action
            android:id="@+id/action_navStepFragmentOne_to_navStepFragmentTwo"
            app:destination="@id/navStepFragmentTwo" />
        <argument
            android:name="flowStepNumber"
            app:argType="integer"
            android:defaultValue="1" />
    </fragment>

    <fragment
        android:id="@+id/navStepFragmentTwo"
        android:name="com.yy.onepiece.jetpackpractice.ui.NavStepFragment"
        android:label="NavStepFragment"
        tools:layout="@layout/fragment_nav_step_two">
        <argument
            android:name="flowStepNumber"
            app:argType="integer"
            android:defaultValue="2" />
        <action
            android:id="@+id/action_navStepFragmentTwo_to_navHomeFragment"
            app:popUpTo="@id/navHomeFragment" />
    </fragment>
</navigation>

其相应的Destinations视图为:
Navigation使用指南
根标签会被解析成NavGragh对象,其属性app:startDestination指明了第一个显示的Destination,即NavHomeFragment。<fragment>标签会被解析成Destination对象,以其id为key保存在NavGragh对象的mNodes中,<action>标签定义了跳转,会被解析成NavAction对象,以其id为key保存在Destination对象的mActions中。

3 通过Destination跳转

val navOptions = navOptions {
            anim {
                enter = R.anim.slide_in_right
                exit = R.anim.slide_out_left
                popEnter = R.anim.slide_in_right
                popExit = R.anim.slide_out_left
            }
        }
        binding.navigateDestinationButton.setOnClickListener {
            findNavController().navigate(R.id.navStepFragmentOne, null, navOptions)
        }

navOptions定义了进入和退出动画,这里的R.id.navStepFragmentOne即为第一个NavStepFragment的id,这种情况下即是没有定义<action>也是可以跳转的。

4 通过Action跳转

binding.navigateActionButton.setOnClickListener {
            findNavController().navigate(
                R.id.action_navHomeFragment_to_navStepFragment,
                null,
                navOptions
            )
        }

这里的 R.id.action_navHomeFragment_to_navStepFragment是<action>的id,这种情况必须要定义<action>

5 带参数跳转

Navigation组件提供了名为safe args的Gradle Plugin来产生简单的对象和编译生成一些类来安全地访问destinations和actions中定义的arguments。要实现带参数跳转,第一步必须要引入该Plugin:

//project build.gradle
dependencies {
        classpath deps.navigation.safe_args
    }
   
//project versions.gradle
navigation.safe_args = "androidx.navigation:navigation-safe-args-gradle-plugin:$versions.navigation"

// app build.gradle
apply plugin: 'androidx.navigation.safeargs.kotlin'

第二步是在<fragment>标签下定义<argument>标签:

<argument
            android:name="flowStepNumber"
            app:argType="integer"
            android:defaultValue="1" />

第三步通过action跳转并传参:

//NavHomeFragment.kt
binding.navigateDestinationButton.setOnClickListener {
            val actionNavHomeFragmentToNavStepFragment =
                NavHomeFragmentDirections.actionNavHomeFragmentToNavStepFragment(3)
            findNavController().navigate(actionNavHomeFragmentToNavStepFragment, navOptions)
        }

NavHomeFragmentDirections类就是编译生成的,实现了接口NavDirections,看一下这个接口的注释: An interface that describes a navigation operation: action’s id and arguments。
第四步是获取该参数:

//NavStepFragment.kt
private val layoutId: Int
        get() {
            flowStepNumber = arguments?.get("flowStepNumber") as? Int ?: 1
            Log.d(TAG, "flowStepNumber: $flowStepNumber")
            return if (flowStepNumber == 2) R.layout.fragment_nav_step_two
            else R.layout.fragment_nav_step_one
        }

使用NavigationUI进行导航

1. Options menu在NavigationUI中的使用

1.1 定义资源文件

首先需要定义<menu>资源文件:

//overflow_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <item
        android:id="@id/nav_setting"
        android:icon="@drawable/ic_settings"
        android:menuCategory="secondary"
        android:title="@string/settings" />
</menu>

这里的id指向的是定义在<navigaiton>中的<fragment>的id:

<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/mobile_navigation"
    app:startDestination="@id/navHomeFragment">
    //...
    <fragment
        android:id="@+id/nav_setting"
        android:name="com.yy.onepiece.jetpackpractice.ui.NavSettingFragment"
        android:label="NavDeepLinkFragment"
        tools:layout="@layout/fragment_nav_setting"/>
</navigation>

1.2 创建menu

//NavigationActivity.kt
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
        menuInflater.inflate(R.menu.overflow_menu, menu)
        return super.onCreateOptionsMenu(menu)
    }

此时ToolBar上会展示menu的入口按钮,点击该按钮会显示该menu,但是点击menu的item还不能跳转到@id/nav_setting,需要让NavigationUI来处理onOptionsItemSelected()回调,如果menu的item的目的并不是跳转,那就交给父类处理。

1.3 点击item实现跳转

//NavigationActivity.kt
override fun onOptionsItemSelected(item: MenuItem): Boolean {
        return item.onNavDestinationSelected(findNavController(R.id.my_nav_host_fragment))
                || super.onOptionsItemSelected(item)
    }

2. 使用NavigationUI来实现底部导航

2.1 在布局中加上BottomNavigationView

//activity_navigation.xml
<androidx.drawerlayout.widget.DrawerLayout
    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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <androidx.appcompat.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorPrimary"
            android:theme="@style/ThemeOverlay.MaterialComponents.Dark.ActionBar"
            app:layout_constraintTop_toTopOf="parent"/>

        <fragment
            android:id="@+id/my_nav_host_fragment"
            android:name="androidx.navigation.fragment.NavHostFragment"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            app:layout_constraintTop_toBottomOf="@id/toolbar"
            app:layout_constraintBottom_toBottomOf="parent"
            app:defaultNavHost="true"
            app:navGraph="@navigation/mobile_navigation" />

        <com.google.android.material.bottomnavigation.BottomNavigationView
            android:id="@+id/bottom_nav_view"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorAccent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:menu="@menu/bottom_nav_menu" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</androidx.drawerlayout.widget.DrawerLayout>

menu属性指向了底部导航栏的样式:

//bottom_nav_menu.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:id="@id/navHomeFragment"
        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>

id属性指向定义在<navigation>中的<fragment>的id值,此时BottomNavigationView已经可以正确展示了,但是还不支持点击跳转。
Navigation使用指南

2.2 NavigationUI处理底部导航跳转

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

3. 使用NavigationUI来实现Drawer跳转

3.1 在资源文件中加上NavigationView

<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
    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/drawer_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">
		//...
    </androidx.constraintlayout.widget.ConstraintLayout>

    <com.google.android.material.navigation.NavigationView
        android:id="@+id/nav_view"
        android:layout_width="wrap_content"
        android:layout_height="match_parent"
        android:layout_gravity="start"
        app:menu="@menu/nav_drawer_menu" />

</androidx.drawerlayout.widget.DrawerLayout>

配置完手指右滑已经可以打开Drawer了,但是还不能实现item点击跳转。

3.2 NavigationUI处理Drawer item点击跳转

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

如上图,此时ActionBar上还没有左上角的图标,虽然实现了跳转,但是Drawer点击跳转到某个Fragment后并没有影响到ActionBar,要建立这一联系,要通过AppBarConfiguration。

3.3 创建包含一些列顶层destination的AppBarConfiguration

//NavigationActivity.kt
private fun initView() {
        val toolbar = findViewById<Toolbar>(R.id.toolbar)
        setSupportActionBar(toolbar)

        val host: NavHostFragment = supportFragmentManager
            .findFragmentById(R.id.my_nav_host_fragment) as NavHostFragment? ?: return
        val navController = host.navController

        val drawerLayout: DrawerLayout? = findViewById(R.id.drawer_layout)
        appBarConfiguration = AppBarConfiguration(
            setOf(R.id.navHomeFragment, R.id.deeplink_dest),
            drawerLayout)
        setupActionBar(navController, appBarConfiguration)
    }

private fun setupActionBar(
        navController: NavController,
        appBarConfig: AppBarConfiguration
    ) {
        setupActionBarWithNavController(navController, appBarConfig)
    }

如上图所示,经过这一操作,在处于顶层destination时会在ActionBar左上角显示一个图标,在处于非顶层destination时会显示箭头图标,如下图所示:
Navigation使用指南
此时左上角的箭头图标还不能实现点击返回。

3.4 处理点击返回事件

override fun onSupportNavigateUp(): Boolean {
        return findNavController(R.id.my_nav_host_fragment).navigateUp()
    }

处理Deeplink

1. 定义

在需要处理deepllink的destination中定义<deeplink>,如果需要接收数据,还需定义<argument>,这两处的参数名必须一样。

<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/mobile_navigation"
    app:startDestination="@id/navHomeFragment">
    //...
    <fragment
        android:id="@+id/deeplink_dest"
        android:name="com.yy.onepiece.jetpackpractice.ui.NavDeepLinkFragment"
        android:label="NavDeepLinkFragment"
        tools:layout="@layout/fragment_nav_deep_link">
        <argument android:name="nav_arg"
            android:defaultValue="hello navigation"/>
        <deepLink app:uri="hello.navigation.com/{nav_arg}"/>
    </fragment>
</navigation>

这里uri需要注意一下几点:

  1. uri可以省略scheme,即http://www.baidu.com和www.baidu.com是匹配的。
  2. 可以使用占位符:{placeholder}。
  3. 可以使用通配符.*,它可以匹配零个或多个字符。

2. 在manifest中配置<nav-graph>

	<application>
		//...
		<activity android:name=".ui.NavigationActivity"
            android:label="@string/navigation_activity_title"
            android:theme="@style/AppTheme" >
            <nav-graph android:value="@navigation/mobile_navigation" />
        </activity>
    </application>

3 接收参数

//NavDeepLinkFragment.kt
override fun initView() {
        val navArg = arguments?.getString("nav_arg")
        Log.d(TAG, "navArg: $navArg")
        binding.deepLinkText.text = navArg
    }

使用adb命令来启动app:

adb -s YourDeviceId shell am start -a android.intent.action.VIEW -d "http://hello.navigation.com/testUrl"

结果如下图所示,点击我们的App图标后会跳转到NavDeepLinkFragment,并且当前堆栈中没有MainActivity实例,只有一个NavigationActivity实例。
Navigation使用指南

本文地址:https://blog.csdn.net/weixin_40888127/article/details/107858926

相关标签: JetPack