当前位置: 移动技术网 > 移动技术>移动开发>Android > Navigation使用指南

Navigation使用指南

2020年08月14日  | 移动技术网移动技术  | 我要评论
概述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视图为:
在这里插入图片描述
根标签会被解析成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已经可以正确展示了,但是还不支持点击跳转。
在这里插入图片描述

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时会显示箭头图标,如下图所示:
在这里插入图片描述
此时左上角的箭头图标还不能实现点击返回。

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实例。
在这里插入图片描述

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

如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网