关键词:
概述
Navigation是采用一个Activity和多个Fragment形式设计的Ui架构模式,但是众所周知,Fragment的管理一直是个麻烦事,需要通过FragmentManager和FragmentTransaction来管理Fragment之间的切换。所以Google提供了一套Navigation用来管理Fragment相互间的跳转等逻辑。我们先看下Navigation的优势:
-
处理 Fragment 事务。
-
默认情况下,正确处理往返操作。
-
为动画和转换提供标准化资源。
-
实现和处理深层链接。
-
包括导航界面模式(例如抽屉式导航栏和底部导航),用户只需完成极少的额外工作。
-
Safe Args - 可在目标之间导航和传递数据时提供类型安全的 Gradle 插件。
-
ViewModel支持 - 您可以将ViewModel的范围限定为导航图,以在图表的目标之间共享与界面相关的数据。
正式介绍前,我们需要了解Navigation是由哪几部分组成的,现在我们看一下:
-
NavHostFragment:一种特殊的Fragment,用于承载导航内容的容器。
-
Navigation Graph:一个包含所有导航和页面关系相关的 XML资源。
-
NavController:管理应用导航的对象,实现Fragment之间的跳转等操作。
Navigation使用
Navigation Graph
首先我们在build.gradle 文件中新增navigation的依赖:
// Kotlin
implementation("androidx.navigation:navigation-fragment-ktx:$lifecycle_version")
implementation("androidx.navigation:navigation-ui-ktx:$lifecycle_version")
然后我们需要新建一个Navigation,点击Project->Res->New Android Resource File,Resource Type选择Navigation,如下图:
打开新建的nav_graph.xml文件,在Design界面可以看到目前还没有内容,可以依次点击[New Destination]图标,然后点击[Create new destination],即可快速创建新的Fragment,这里作次示例,因为本身之前我已经新建好多个Fragment了,就不单独示范了。
我们可以点击视图的小圆点,建议页面的绑定关系。我这里已经建立好多个页面的绑定关系,同时你可以切换至code模块,本人代码如下:
<?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/jetpack_nav"
app:startDestination="@id/navigationFragment">
<fragment
android:id="@+id/navigationFragment"
android:name="com.silence.apmprojetct.NavigationFragment"
android:label="fragment_navigation"
tools:layout="@layout/fragment_navigation">
<action
android:id="@+id/action_navigationFragment_to_blankFragment"
app:destination="@id/blankFragment" />
<action
android:id="@+id/action_navigationFragment_to_threeFragment"
app:destination="@+id/threeFragment" />
</fragment>
<fragment
android:id="@+id/blankFragment"
android:name="com.silence.apmprojetct.SecondFragment"
android:label="fragment_blank"
tools:layout="@layout/fragment_second">
<action
android:id="@+id/action_blankFragment_to_threeFragment"
app:destination="@id/threeFragment" />
</fragment>
<fragment
android:id="@+id/threeFragment"
android:name="com.silence.apmprojetct.ThreeFragment"
android:label="fragment_three"
tools:layout="@layout/fragment_three">
<action
android:id="@+id/action_threeFragment_to_navigationFragment"
app:destination="@+id/navigationFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment>
</navigation>
我们看下几个标签的含义:
- navigation:根标签,通过startDestination配置指定默认的启动页面。
- fragment: fragment标签代表一个fragment。
- action:action标签定义了页面跳转的行为,destination标签定义跳转的目标页,里面还有启动模式的相关设置等等。
NavHostFragment
而讲到这里,我们只概括前面三个模块其中之一Navigation Graph。我们知道Fragment需要一个Activity才能正常运行,但是我们如何在我们的Activity指定我们上面startDestination呢。这就需要用到了NavHostFragment。我们看下我们Activity的布局代码:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".NavigationActivity">
<fragment
android:id="@+id/fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/jetpack_nav" />
</FrameLayout>
我们可以看到布局里定义了一个Fragment,并且它使用的就是NavHostFragment,可以看到navGraph属性绑定了我们刚刚写的Navigation文件。而defaultNavHost就是做到了自动实现了页面间的返回操作。如果没有这个属性,点击返回的时候就是直接关闭当前Activity了。当然,其内部Fragment的点击事件我们可以自己控制。
然后我们可以运行程序,可以看到默认展示的是NavigationFragment页面,这是因为NavigationActivity的布局文件中配置了NavHostFragment,并且给NavHostFragment指定了默认展示的页面为NavigationFragment。
NavController
NavController主要用来管理fragment之间的跳转,每个NavHost均有自己的相应NavController。然后可以使用它的navigate或者navigateUp方法来进行页面之间的路由操作。比如我这边的NavigationFragment需要跳转SecondFragment。我们可以这么写:
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View?
val view = inflater.inflate(R.layout.fragment_navigation, container, false)
view.findViewById<TextView>(R.id.next).setOnClickListener
findNavController().navigate(R.id.action_navigationFragment_to_blankFragment)
return view
也就是通过navigate指向前面我们在action定义的id。即可完成跳转。
参数传递
Bundle数据传递
我们可以创建一个Bundle对象,然后使用navigate()将它传递给目的地,代码如下:
val bundle = Bundle()
bundle.putString(TITLE, getString(R.string.three_fragment_navigation_bundle_label))
findNavController().navigate(R.id.action_blankFragment_to_threeFragment, bundle)
使用 Safe Args 传递安全的数据
这是官方为了navigation数据传递安全提供的一个数据传递方式,首先我们需要在根目录的build.gradle文件中添加如下依赖:
dependencies
def nav_version = "2.3.3"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
然后我们在模块的build.gradle文件中新增如下:
//kotlin
apply plugin: "androidx.navigation.safeargs.kotlin"
//java
apply plugin: 'androidx.navigation.safeargs'
然后我们ReBuild项目,可以看到在build目录中生成了如下文件:
然后我们修改我们的代码,比如我需要在ThreeFragment页面新增一个参数,那么我们需要在navigation文件中新增如下代码:
<fragment
android:id="@+id/threeFragment"
android:name="com.silence.apmprojetct.ThreeFragment"
android:label="fragment_three"
tools:layout="@layout/fragment_three">
<action
android:id="@+id/action_threeFragment_to_navigationFragment"
app:destination="@+id/navigationFragment" />
<argument
android:name="key"
app:argType="string"
android:defaultValue="safeArgs" />
</fragment>
然后我们在需要跳转的时候修改下代码,比如我这里SecondFragment会跳转至ThreeFragment。那么我们只需要这么写:
view.findViewById<TextView>(R.id.next).setOnClickListener
// val bundle = Bundle()
// bundle.putString(TITLE, getString(R.string.three_fragment_navigation_bundle_label))
// findNavController().navigate(R.id.action_blankFragment_to_threeFragment, bundle)
var secondFragment =
SecondFragmentDirections.actionBlankFragmentToThreeFragment("safe传递的数据")
findNavController().navigate(secondFragment)
我们在看下如何在ThreeFragment里接收数据:
view.findViewById<TextView>(R.id.title).text = arguments?.let
ThreeFragmentArgs.fromBundle(it).key
其他场景
堆栈处理
前面我们说到了正常的页面启动,以及如何挟带参数跳转。现在有个场景,比如我A-》B-》C之后,然后直接回到A。并且清栈,也就是在A页面返回就直接关闭当前的Activity了。我们看一下,目前我们的代码执行后效果会如何:
可以看出来我们每次打开新的fragment就相当于新开了一个堆栈。我们可记得我们配置页面的时候有一个action属性。我们把action的代码改成如下:
<fragment
android:id="@+id/threeFragment"
android:name="com.silence.apmprojetct.ThreeFragment"
android:label="fragment_three"
tools:layout="@layout/fragment_three">
<action
android:id="@+id/action_threeFragment_to_navigationFragment"
app:destination="@+id/navigationFragment"
app:popUpTo="@+id/navigationFragment"
app:popUpToInclusive="true" />
</fragment>
我们运行一下,在看一下效果:
哎,可以了。
其实action还有一个属性launchSingleTop的设置,这个看具体场景使用即可。
指定startDestination
我们想一下,如何指定startDestination的Fragment呢。上面介绍过,activity要绑定NavHostFragment。而NavHostFragment要指定startDestination。在我们在navigaiton指定了fragment后,可以修改么。或者说,我可以动态改变他的startDestination嘛。其实是可以的。我们知道startDestination是由navGraph指定的。那么我们只需要修改activity的代码,就可以做到了。具体示例如下:
override fun onCreate(savedInstanceState: Bundle?)
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_navigation)
val startDestinationID = R.id.blankFragment
val navController = Navigation.findNavController(this, R.id.fragment)
val navInflater = navController.navInflater
val navGraph = navInflater.inflate(R.navigation.jetpack_nav)
navGraph.startDestination = startDestinationID
navController.graph = navGraph
那我们执行一下,看下效果:
我这边指定了SecondFragment成功了。那具体指定哪一个,由你自己的业务场景而定。
总结
本文我们主要介绍了Jetpack中的导航组件Navigation的组件,从普通的跳转,已经挟带参数跳转,甚至一些特殊场景的跳转逻辑。关于一些其他场景,我这里不过多介绍,因为目前正常的跳转场景基本已经了解了。如果你想知道更多的使用场景,请查看官方文档。
本文首发于我的个人博客:Android Jetpack导航组件——Navigation的使用
更多文章请关注我的公众号:码农职场
androidjetpack-使用navigation管理页面跳转(代码片段)
在今年的IO大会上,发布了一套叫AndroidJetpack的程序库。AndroidJetpack里的组件大部分我们都接触过了,其中也有一些全新的组件,其中一个就是Navigation。简介Navigation是用来管理APP里页面跳转的。起初,我以为它是用来代替startActiv... 查看详情
jetpack学习-navigation(代码片段)
...支持用户导航、进入和退出应用中不同内容片段的交互。AndroidJetpack的导航组件可帮助您实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。导航组件还通过遵循一套既定原则来... 查看详情
官方架构组件navigation管理fragment框架(代码片段)
...使用kotlin语言特性时,可以让你更有效率。消除样板代码AndroidJetpack管理乏味的活动,例如后台任务、导航和生命周期管理,你可以专注于让你的app更棒的东西。构建高质量、健壮的app基于现代设计实践,AndroidJetpack组件可以减... 查看详情
androidjetpack之navigation源码分析(代码片段)
AndroidJetpack之Navigation源码分析AndroidNavigation简介关于Fragment的基础篇:Fragment基础篇官方指导地址:官方指地址Githubdemo地址:demo使用Navigation可以管理APP页面跳转。Navigation不部分情况下作用于Fragment中,使用Navigation... 查看详情
使用 React Navigation 导航堆栈时重新渲染组件
】使用ReactNavigation导航堆栈时重新渲染组件【英文标题】:Re-rendercomponentwhennavigatingthestackwithReactNavigation【发布时间】:2019-03-1905:46:31【问题描述】:我目前正在使用react-navigation进行堆栈和选项卡导航。是否可以在用户每次导航... 查看详情
React Native Navigation:从 React 组件外部导航到屏幕
】ReactNativeNavigation:从React组件外部导航到屏幕【英文标题】:ReactNativeNavigation:NavigatetoascreenfromoutsideaReactcomponent【发布时间】:2021-08-1914:08:21【问题描述】:https://wix.github.io/react-native-navigation/docs/basic-navigation#navigating 查看详情
Android Navigation 组件在图表之间导航
】AndroidNavigation组件在图表之间导航【英文标题】:AndroidNavigationComponentnavigatebetweengraphs【发布时间】:2020-01-1508:56:07【问题描述】:我有两个导航图nav_graph_red和nav_graph_blue以及两个活动ActivityRed和ActivityBlue。在每个导航图中,我... 查看详情
导航编辑器初始化失败
...-03-2502:26:10【问题描述】:突然,AndroidStudio开始提示使用AndroidJetpack的Navigation需要安装一个模块android.arch.navigation:navigation-fragment:+.。这是整个模块名称。两个问题。第一,我已经安装了它,版本1.0.0al 查看详情
Android Jetpack Navigation - 如何从抽屉菜单项导航到嵌套导航图
】AndroidJetpackNavigation-如何从抽屉菜单项导航到嵌套导航图【英文标题】:AndroidJetpackNavigation-Howtonavigatetonestednavgraphfromdrawermenuitem【发布时间】:2020-07-0516:29:09【问题描述】:我很好奇如何使用AndroidJetpack的导航图从抽屉布局中的... 查看详情
如何通过 React Navigation 将导航道具传入功能组件屏幕?
】如何通过ReactNavigation将导航道具传入功能组件屏幕?【英文标题】:HowtopassinnavigationpropsintoafunctionalcomponentscreenthroughReactNavigation?【发布时间】:2020-08-2900:33:48【问题描述】:我有一个带有自定义“任务”的ReactNativeFlatList的功... 查看详情
android jetpack 导航仪器测试在返回导航上失败
】androidjetpack导航仪器测试在返回导航上失败【英文标题】:androidjetpacknavigationinstrumentedtestfailonbacknavigation【发布时间】:2021-06-1023:02:01【问题描述】:我使用jetpackNavigation组件(androidx.navigation)创建了一个简单的两个片段示例应... 查看详情
TypeError:未定义不是对象(评估“navigation.navigate”),无法从放置在屏幕上的组件导航到另一个屏幕
...评估“navigation.navigate”),无法从放置在屏幕上的组件导航到另一个屏幕【英文标题】:TypeError:undefinedisnotanobject(evaluating\'navigation.navigate\'),cannotnavigatefromcomponentplacedonscreentoanotherscreen【发布时间】:2021-11-0923:03:13 查看详情
reactnative导航器之react-navigation使用
在上一节Navigation组件,我们使用系统提供的导航组件做了一个跳转的例子,不过其实战能力不强,这里推荐一个超牛逼的第三方库:react-navigation。在讲react-navigation之前,我们先看一下常用的导航组件。导航控件常见的导航主要... 查看详情
如何将道具传递给 React Navigation 导航器中的组件?
】如何将道具传递给ReactNavigation导航器中的组件?【英文标题】:HowtopasspropstocomponentinsideaReactNavigationnavigator?【发布时间】:2018-10-3002:56:39【问题描述】:我正在尝试将props传递给已通过调用create...Navigator调用包装的组件,即//Se... 查看详情
navigation导航组件的使用:内容承载容器navhostfragment加载原理(代码片段)
Navigation导航组件由以下三个关键部分组成:导航图:在一个集中的位置包含所有与导航相关的信息的XML资源。这包括应用程序中所有单独的内容区域,称为目的地,以及用户可以在应用程序中使用的可能路径。Nav... 查看详情
v2 Navigation.showModal 创建新组件实例但实际上并未导航到它
】v2Navigation.showModal创建新组件实例但实际上并未导航到它【英文标题】:v2Navigation.showModalcreatingnewcomponentinstancebutnotactuallynavigatingtoit【发布时间】:2019-06-2609:08:41【问题描述】:我正在尝试使用react-native-navigationv2(版本2.8.0)... 查看详情
是否可以使用 Android 导航架构组件(Android Jetpack)有条件地设置 startDestination?
】是否可以使用Android导航架构组件(AndroidJetpack)有条件地设置startDestination?【英文标题】:IsitpossibletosetstartDestinationconditionallyusingAndroidNavigationArchitectureComponent(AndroidJetpack)?【发布时间】:2019-01-2611:56:27【问题描述】:我正在... 查看详情
react-native中导航组件react-navigation的使用(代码片段)
...头的话最近使用React-Native开发新应用,一开始使用的导航器是navigator,后来发现navigator有很多不足之处,而且官方也建议使用react-navigation来进行开发,所以现在转为使用react-navigation开发新应用了。总览ReactNative中... 查看详情