作者
大家好,我叫小琪;
自己16年毕业于中南林业科技大学软件工程专业,毕业后在教育职业做安卓开发,后来于19年10月加入37手游安卓团队;
现在首要担任国内发行安卓相关开发,同时兼顾内部几款App开发。
目录
-
navigation——入门篇(本章讲解)
-
navigation——进阶篇
-
navigation——实战篇 (敬请期待…)
前语
在日常开发中,越来越多的会运用到一个activity嵌套多个fragment的场景,典型的例子便是app的首页,一般都会由一个activity+多个子tab组成,那关于Fragment的显现、隐藏等咱们通常都是经过FragmentManager进行办理,但这种办法很简略形成代码臃肿,难以维护。
而经过Jetpack的导航组件——Navigation,就能够很便利的办理各fragment之间的切换,让开发变得更简略。
组成三要素
Navigation graph
一个包含所有导航相关信息的 XML 资源
NavHostFragment
一种特别的Fragment,用于承载导航内容的容器
NavController
办理应用导航的目标,完成Fragment之间的跳转等操作
基本运用
引进依靠
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.1'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.1'
创立导航视图
首先保证AndroidStudio为3.3以上
1.右键res,点击New -> Android Resource Directory
2.在出现的面板第二行Resource type 下拉列表中挑选 Navigation,然后点击 OK
3.res目录下会多出一个navigation的资源目录,右键该目录,点击New -> Navigation Resource File,输入需求新建的资源文件名,这儿命名nav_graph,点击ok,一个nav_graph.xml就创立好了。
装备graph
新建好的nav_graph.xml切换到design形式下,点击2处的加号,挑选Create new destination,即可快速创立新的Fragment,这儿分别新建了FragmentA、FragmentB、FragmentC三个fragment
建好后,可经过手动装备页面之间的跳转联系,点击某个页面,右边会出现一个小圆点,拖曳小圆点指向跳转的页面,这儿设置跳转的联系为FragmentA -> FragmentB -> FragmentC。
切换到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/nav_graph"
app:startDestination="@id/fragmentA">
<fragment
android:id="@+id/fragmentA"
android:name="com.example.testnavigation.FragmentA"
android:label="fragment_a"
tools:layout="@layout/fragment_a" >
<action
android:id="@+id/action_fragmentA_to_fragmentB2"
app:destination="@id/fragmentB" />
</fragment>
<fragment
android:id="@+id/fragmentB"
android:name="com.example.testnavigation.FragmentB"
android:label="fragment_b"
tools:layout="@layout/fragment_b" >
<action
android:id="@+id/action_fragmentB_to_fragmentC2"
app:destination="@id/fragmentC" />
</fragment>
<fragment
android:id="@+id/fragmentC"
android:name="com.example.testnavigation.FragmentC"
android:label="fragment_c"
tools:layout="@layout/fragment_c" />
</navigation>
- navigation是根标签,经过startDestination装备默许启动的第一个页面,这儿装备的是FragmentA
- fragment标签代表一个fragment,其实这儿不只能够装备fragment,也能够装备activity,乃至还能够自界说(暂不讨论,后续会讲到)
- action标签界说了页面跳转的行为,相当于上图中的每条线,destination界说跳转的目标页,还能够界说跳转时的动画等等
增加NavHostFragment
在MainActivity的布局文件中装备NavHostFragment
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 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:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<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:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
- android:name指定NavHostFragment
- app:navGraph指定导航视图,即建好的nav_graph.xml
- app:defaultNavHost=true 意思是能够拦截体系的回来键,能够理解为默许给fragment完成了回来键的功用,这样在fragment的跳转过程中,当咱们按回来键时,就能够使得fragment跟activity一样能够回到上一个页面了
现在咱们运转程序,就能够正常跑起来了,而且看到了FragmentA展现的页面,这是由于MainActivity的布局文件中装备了NavHostFragment,而且给NavHostFragment指定了导航视图,而导航视图中经过startDestination指定了默许展现FragmentA。
经过NavController 办理fragment之间的跳转
上面提到三个fragment之间的跳转联系是FragmentA -> FragmentB -> FragmentC,而且现已能够展现了FragmentA,那怎样跳转到FragmentB呢,这就需求用到NavController 了
翻开FragmentA类,给布局中的TextView界说一个点击事情
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
tv.setOnClickListener {
val navController = Navigation.findNavController(it)
navController.navigate(R.id.action_fragmentA_to_fragmentB2)
}
}
如果发现不能主动导入布局文件,大概率是要给app.build增加插件‘kotlin-android-extensions’
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
AndroidStudio4.1以后改成了
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-android-extensions'
}
能够看到,经过navController办理fragment的跳转非常简略,首先得到navController目标,然后调用它的navigate办法,传入前面nav_graph中界说的action的id即可。
按同样的办法给FragmentB中的TextView也设置一个点击事情,使得点击时跳转到FragmentC
运转程序,FragmentA -> FragmentB -> FragmentC,此时按回来键,也是一个一个页面回来,如果把前面的app:defaultNavHost设置为false,按回来键后会发现直接回来到桌面了,现在能体会到app:defaultNavHost这个特点的意义了吧。
更多用法
在修改nav_graph的时分,action特点除了设置目标页外,还能够设置动画、页面间参数传递、fragment回退栈办理等
动画
-
enterAnim: 跳转时的目标页面动画
-
exitAnim: 跳转时的原页面动画
-
popEnterAnim: 回退时的目标页面动画
-
popExitAnim:回退时的原页面动画
装备动画后会发现action多了四个动画相关的特点
<fragment
android:id="@+id/fragmentA"
android:name="com.example.testnavigation.FragmentA"
android:label="fragment_a"
tools:layout="@layout/fragment_a" >
<action
android:id="@+id/action_fragmentA_to_fragmentB2"
app:destination="@id/fragmentB"
app:enterAnim="@anim/enter_in"
app:exitAnim="@anim/enter_out"
app:popEnterAnim="@anim/exit_in"
app:popExitAnim="@anim/exit_out" />
</fragment>
参数
上面的例子中介绍fragment之间的跳转,当然也能够支撑参数传递。
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
tv.setOnClickListener {
val navController = Navigation.findNavController(it)
val bundle = Bundle()
bundle.putString("key", "test")
navController.navigate(R.id.action_fragmentA_to_fragmentB2, bundle)
}
}
参数传递推荐运用谷歌官方的safeArgs,safe args与传统传参办法相比,好处在于安全的参数类型,而且经过谷歌官方的支撑,能很便利的进行参数传值。
在项目的根build.gradle下增加插件
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.1"
buildscript {
ext.kotlin_version = "1.3.72"
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.1.1"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.1"
}
}
allprojects {
repositories {
google()
jcenter()
}
}
然后在app的build.gradle中引用 ‘androidx.navigation.safeargs.kotlin’
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'androidx.navigation.safeargs.kotlin'
AS4.1以后:
plugins {
id 'com.android.application'
id 'kotlin-android'
id 'kotlin-android-extensions'
id 'androidx.navigation.safeargs.kotlin'
}
增加完插件后,回到nav_graph,切到design形式,给目标页面增加需求接纳的参数,这儿需求在FragmentA跳转到FragmentB时传参数,所以给FragmentB设置参数,点击FragmentB,点击右侧面板的Arguments右侧的+,输入参数的key值,指定参数类型和默许值,即可快速增加参数
增加完后,rebuild一下工程,safeArgs会主动生成一些代码,在/build/generated/source/navigation-args目录下能够看到
safeArgs会依据nav_graph中的fragment标签生成对应的类,action标签会以“类名+Directions”命名,argument标签会以“类名+Args”命名。
运用safeArgs后,传递参数是这样的
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
tv.setOnClickListener {
val navController = Navigation.findNavController(it)
//经过safeArgs传递参数
val navDestination = FragmentADirections.actionFragmentAToFragmentB2("test")
navController.navigate(navDestination)
// 一般办法传递参数
// val bundle = Bundle()
// bundle.putString("key", "test")
// navController.navigate(R.id.action_fragmentA_to_fragmentB2, bundle)
}
}
接纳参数是这样的
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
arguments?.let {
val value = FragmentBArgs.fromBundle(it).key
.......
}
.......
}
栈办理
点击destination,右侧面板中还能够看到popUpTo、popUpToInclusive、launchSingleTop
- launchSingleTop:如果栈中现已包含了指定要跳转的界面,那么只会保存一个,不指定则栈中会出现两个界面相同的Fragment数据,能够理解为类似activity的singleTop,即栈顶复用形式,但又有点不一样,比方FragmentA@1 -> FragmentA@2,FragmentA@1会被毁掉,但如果是FragmentA@01>FragmentB@02>FragmentA@03,FragmentA@1不会被毁掉。
- popUpTo(tag):表明跳转到某个tag,并将tag之上的元素出栈。
- popUpToInclusive:为true表明会弹出tag,false则不会
例子:FragmentA -> FragmentB -> FragmentC -> FragmentA
设置FragmentC -> FragmentA 的action为popUpTo=FragmentA ,popUpToInclusive=false,那么栈内元素变化为
最后会发现需求按两次回来键才会回退到桌面
设置popUpToInclusive=true时,栈内元素变化为
此时只需求按一次回来键就回退到桌面了,从中能够体会到popUpTo和popUpToInclusive的意义了吧。
deeplink
深度链接,便是能够直接跳转到某个页面。navigation创立深度链接能够经过显现和隐式两种办法
按之前的办法新建一个需求经过深度链接翻开的目标页面FragmentDeepLink,
接下来为它创立一个deeplink
nav_graph.xml相应的在生成了如下代码
<fragment
android:id="@+id/fragmentDeepLink"
android:name="com.example.testnavigation.FragmentDeepLink"
android:label="fragment_deep_link"
tools:layout="@layout/fragment_deep_link">
<argument
android:name="key"
android:defaultValue="测验"
app:argType="string" />
<deepLink
android:id="@+id/deepLink"
app:uri="www.deeplink.com/{id}" />
</fragment>
-
显现深度链接
显现深层链接运用PendingIntent来导航到特定页面,比方点击通知栏,快速翻开目标页面。
tv_deeplink.setOnClickListener { //显现深度链接 val notificationManager = NotificationManagerCompat.from(requireContext()) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val importance = NotificationManager.IMPORTANCE_DEFAULT val channel = NotificationChannel(channelId, channelName, importance) channel.description = "deeplink" notificationManager.createNotificationChannel(channel) } val navController = Navigation.findNavController(it) val deepLinkBuilder = navController.createDeepLink() val bundle = Bundle() bundle.putString("key", "deeplink") val pendingIntent = deepLinkBuilder //传入graph资源文件 .setGraph(R.navigation.nav_graph) //传入参数 .setArguments(bundle) //传入需求经过深度链接翻开的目标页面 .setDestination(R.id.fragmentDeepLink) .createPendingIntent() val builder = NotificationCompat.Builder(requireContext(), channelId) .setSmallIcon(R.drawable.ic_launcher_foreground) .setContentTitle("测验deepLink") .setContentText("哈哈哈") .setContentIntent(pendingIntent) .setAutoCancel(true) .setPriority(NotificationCompat.PRIORITY_DEFAULT) notificationManager.notify(1, builder.build()) }
-
隐式深度链接
隐式链接是当用户点击某个链接的时分,经过URI跳转到某个页面,刚刚现已为nav_graph.xml中的FragmentDeepLink增加了
<deepLink app:uri="www.deeplink.com/{id}" />
该uri没有声明是http仍是https,那么这两个都能匹配。大括号内的是传递的参数。
AndroidManifest.xml中给FragmentDeepLink所属的activity增加一个
<nav-graph>
特点,这为MainActivity<activity android:name=".MainActivity"> ...... <nav-graph android:value="@navigation/nav_graph"/> </activity>
在build后,经过目录app – > build – > outputs – > apk – > debug – > app-debug.apk查看AndroidManifest.xml中的MainActivity节点下会多出如下代码
<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.deeplink.com" />
<data
android:pathPrefix="/" />
</intent-filter>
Navigation 组件会将 <nav-graph>
元素替换为生成的 <intent-filter>
元素来匹配深层链接。
咱们能够经过adb来测验隐式深层链接的作用,翻开命令行输入
adb shell am start -a android.intent.action.VIEW -d "http://www.deeplink.com/1"
在体系弹出的窗口中,挑选自己的应用翻开,就能跳转到目标页面了。
总结
本篇是navigation的入门篇,首要介绍了navigation的基本运用,下篇将从源码角度,分析navigation是如何做到页面之间跳转的。
结束语
过程中有问题或者需求沟通的同学,能够扫描二维码加好友,然后进群进行问题和技能的沟通等;