前言
前段时间,我在做项目开发的时候对Fragment的管理遇到几个小问题,总觉得在现阶段封装好的Fragment管理器不太优雅。这成为我下决心学习Jetpack在很早之前推出的Navigation库,该库的诞生就是为了能够更加优雅的管理Fragment。在学习新知识时,我比较喜欢将我遇到的知识点与难点写在纸上。但有时由于时间比较紧,记在纸上的东西往往没有那么的详细与具体。所以我决定以后通过以写博客的方式对知识进行一个归纳总结,也希望能帮助到在看这篇博客的你。让我们一起来完善Android的知识体系吧!
image
使用条件
如果您要在 Android Studio 中使用 Navigation 组件,则必须使用 Android Studio 3.3 或更高版本。
添加依赖
要在您的项目中添加 Navigation 支持,请向应用的 build.gradle 文件添加以下依赖项:
dependencies {
//...
implementation "androidx.navigation:navigation-fragment-ktx:2.2.1"
implementation "androidx.navigation:navigation-ui-ktx:2.2.1"
}
使用Navigation的具体流程
项目演示
image
新建三个Framgnet
在配置Navigation之前,我们 创建三个Framgnet用于测试 。代码如下所示:
//第一个Fragment
class Page1Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View?
{
return inflater.inflate(R.layout.fragment_page_1, container, false)
}
}
//第二个Framgnet
class Page2Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View?
{
return inflater.inflate(R.layout.fragment_page_2, container, false)
}
}
//第三个Fragment
class Page3Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View?
{
return inflater.inflate(R.layout.fragment_page_3, container, false)
}
}
配置导航
- 我们需要在res文件夹下新建一个 navigaton 文件夹。
- 在 navigaton 文件夹下创建一个 navigation资源文件 。
- 我们将它起名为 mobile_navigaion.xml 。
如下图所示:
image
在 <navigation> 标签里可以嵌套另一个 <navigation> 标签,也可以新建一个navigation xml文件,通过 <include> 将其引进来。
在mobile_navigation.xml中 添加 上我们刚创建好的三个Fragment。
在 <fragment> 标签下要有i d,name,label 与 layout ,这四个属性一定要 齐全 !代码如下所示:
<?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">
<fragment
android:id="@+id/fragment_page_1_id"
android:name="com.johnlion.navigation.Page1Fragment"
android:label="fragment_page_1_label"
tools:layout="@layout/fragment_page_1" />
<fragment
android:id="@+id/fragment_page_2_id"
android:name="com.johnlion.navigation.Page2Fragment"
android:label="fragment_page_2_label"
tools:layout="@layout/fragment_page_2" />
<fragment
android:id="@+id/fragment_page_3_id"
android:name="com.johnlion.navigation.Page3Fragment"
android:label="fragment_page_1_label"
tools:layout="@layout/fragment_page_3" />
</navigation>
此时我们刚添加完Fragment的mobile_navigation.xml中, <navigation> 标签在android studio编译器中会报 红色警告 ,这是由于我们并未在 Activity布局文件 中添加好 NavHostFragment 。
我们打开Activity布局文件,并在布局文件中添加一个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/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
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/mobile_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>
请注意以下几点:
在添加NavHostFragment的时候编译器并未对NavHostFragment的文件路径、defaultNavHost与navGraph属性进行智能提示。请你不要慌,不要怀疑是否有这些属性或值,坚定准确的把它敲完或复制粘贴成功!
android:name
app:navGraph
app:defaultNavHost="true"
NavHostFragment 简单来讲就是一个 导航界面容器 ,用来展示导航中一系列的 Fragment。
NavHostFragment在Activity布局文件中添加成功后,mobile_navigation.xml中navigation标签的红色警告消失了,取而代之的是一个 黄色的提醒 ,这是由于我们并未在navigation的标签中添加一个“ 起始目的地 "!即我们打开应用的第一张页面。在 <navigation> 标签中添加上:
app:startDestination="@id/fragment_page_1_id"
此时在 Navigation Edit 中,作为起始目的地的Fragment顶部就会多了一个小房子。如下图所示:
image
这样我们创建好的Page1Fragment就会变成我们打开应用显示的第一个屏幕。
“起始目的地”配置好之后,我们需要为每一个fragment配置其的“目的地”。配置“目的地”的方式有 两种 ,一种是在 Navigation Edit 中,拖拉可视化Fragment的箭头来指向它的“目的地”;第二种是直接在 xml 中,在每个Fragment中添加 <action> 标签,并配置好其“目的地”。代码如下所示:
<?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/fragment_page_1_id">
<fragment
android:id="@+id/fragment_page_1_id"
android:name="com.johnlion.navigation.Page1Fragment"
android:label="fragment_page_1_label"
tools:layout="@layout/fragment_page_1">
<action
android:id="@+id/action_page_1_to_page_2"
app:destination="@id/fragment_page_2_id" />
</fragment>
<fragment
android:id="@+id/fragment_page_2_id"
android:name="com.johnlion.navigation.Page2Fragment"
android:label="fragment_page_2_label"
tools:layout="@layout/fragment_page_2">
<action
android:id="@+id/action_page_2_to_page_3"
app:destination="@id/fragment_page_3_id" />
<action
android:id="@+id/action_page_2_to_page_1"
app:popUpTo="@id/fragment_page_1_id" />
</fragment>
<fragment
android:id="@+id/fragment_page_3_id"
android:name="com.johnlion.navigation.Page3Fragment"
android:label="fragment_page_1_label"
tools:layout="@layout/fragment_page_3">
<action
android:id="@+id/action_page_3_to_page_2"
app:popUpTo="@id/fragment_page_2_id" />
</fragment>
</navigation>
应用导航图如下图所示:
image
实现跳转
接下来我们给每个Fragment配置好其对应的跳转事件。代码如下所示:
//第一个Fragment
class Page1Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View?
{
return inflater.inflate(R.layout.fragment_page_1, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_page1.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_page_1_to_page_2)
}
}
}
//第二个Fragment
class Page2Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View?
{
return inflater.inflate(R.layout.fragment_page_2, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_page2_1.setOnClickListener {
Navigation.findNavController(it).navigateUp()
}
btn_page2_2.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_page_2_to_page_3)
}
}
}
//第三个Fragment
class Page3Fragment : Fragment() {
override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View?
{
return inflater.inflate(R.layout.fragment_page_3, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_page3.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_page_3_to_page_2)
}
}
}
从中我们可以看出Fragment之间的跳转用到的 API 为:
- Navigation.findNavController(view).navigate(actionID) 。
- Navigation.findNavController(view).navigateUp() 。
使用Bundle传递参数
代码如下所示:
btn_bundle.setOnClickListener {
val bundle = Bundle()
bundle.putString("key", "value")
Navigation.findNavController(it).navigate(R.id.action_page_1_to_page_2, bundle)
}
通过创建一个 Bundle() 对象,把所要传递的 key:value 放进 bundle 里面,然后我们在的 navigate(...) 方法中把 bundle 传进去,这样我们就可以通过 bundle 来实现参数的传递了。
界面切换动画
我们可以在目的地之间添加上动画的 过渡效果 ,使Fragment与Fragment之前切换不生硬。那么我们来尝试做个 右进左出 的动画吧。
我们先创建所需要的xml文件,在res目录下新建一个anim文件夹,用来存放实现动画的xml文件,然后新建两个动画资源文件,起名为 slide_in_right.xml 、 slide_out_left.xml ,代码如下所示:
//slide_in_right.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="100%"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="0" />
</set>
//slide_out_left
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="-100%"/>
</set>
添加动画文件完成之后我们可以通过 两种 方法来配置Fragment与Fragment之间跳转的动画过渡,一种是在导航中的 <action> 标签下配置,另一种是通过代码中的 navOptions 方法进行配置,动画配置代码如下所示:
//我们对一张Fragment跳转到第二张Fragment进行动画配置
//第一种:xml静态配置
<action
android:id="@+id/action_page_1_to_page_2"
app:destination="@id/fragment_page_2_id"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left" />
//第二种:代码动态配置
val option = navOptions {
anim {
enter = R.anim.slide_in_right
exit = R.anim.slide_out_left
}
}
btn_page1.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_page_1_to_page_2, null, option)
}
在代码中配置需要 注意 : navigate(...) 中第一个参数传入的是 actionID ,第二个参数传入的是 bundle 对象,由于我们并没有新建一个bundle,所以选择传入null,第三个参数则是 navOptions 。
这样我们目的地与目的地之间的过渡动画就添加完成了~
image
到这里,通过运行预览,基本和示例一样效果了
使用 Safe Args 传递安全的数据
官方文档原话:
Navigation 组件具有一个名为 Safe Args 的 Gradle 插件,该插件可以生成简单的 object 和 builder 类,以便以类型安全的方式浏览和访问任何关联的参数。我们强烈建议您将 Safe Args 用于导航和数据传递,因为它可以确保类型安全。
先配置安全插件。
//顶级的build.gradle
buildscript {
repositories {
//...
google()
}
dependencies {
//...
def nav_version = "2.1.0"
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version"
}
}
//应用或模块级的build.gradle
//...
apply plugin: 'com.android.application'
apply plugin: "androidx.navigation.safeargs.kotlin"
android{
//...
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8
}
}
添加安全插件完成后,它会自动生成末尾带有 Directions 的类,该类的名称是在源目的地的名称后面加上“Directions”,该类里面带有原目的地 action 的方法。
image
在官方文档中介绍,Navigation 库支持以下参数类型:
image
那我们来挑上几种类型尝试一遍吧。
首先我们需要在 mobile_navigation.xml 中添加上我们 自定义的参数 ,假如我们打算从 Page1 中把参数传递给 Page2 ,那么我们则需要在 Page2 (接收目的地)的 <fragment> 标签下添加上 <argument> 标签并添加上 名字 、 默认值 、 类型 三种属性。代码如下所示:
<fragment
android:id="@+id/fragment_page_2_id"
android:name="com.johnlion.navigation.Page2Fragment"
android:label="fragment_page_2_label"
tools:layout="@layout/fragment_page_2">
<!--
...
-->
<argument
android:name="myInteger"
android:defaultValue="0"
app:argType="integer" />
<argument
android:name="myString"
android:defaultValue="value"
app:argType="string" />
<argument
android:name="myBoolean"
android:defaultValue="false"
app:argType="boolean" />
</fragment>
添加完成后一定要 reBuild 一下项目,这样安全插件会为我们生成末尾带有“ Args ”的类,里面有我们接收参数目的地即 Page2 获取参数的方法。
image
而且在 Page1FragemntDirections
类中,用来实现action的方法在reBuild后会更新成传递三个默认参数给Page2接收。代码如下所示:
class Page1FragmentDirections private constructor() {
//...
companion object {
fun actionPage1ToPage2(
myInteger: Int = 0,
myString: String = "value",
myBoolean: Boolean = false
): NavDirections = ActionPage1ToPage2(myInteger, myString, myBoolean)
}
}
接下来我们看下如何在代码中 传递 安全的数据。代码如下所示:
//Page1Fragment(传递安全数据)
class Page1Fragment : Fragment() {
//...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_page1.setOnClickListener {
val action = Page1FragmentDirections.actionPage1ToPage2(1, "hello", true)
Navigation.findNavController(it).navigate(action)
}
}
//Page2Fragment(接收安全数据)
class Page2Fragment : Fragment() {
val args: Page2FragmentArgs by navArgs()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//...
//获取的参数名为<argument>中配置好的name
Log.d("data", "integer:" + args.myInteger)
Log.d("data", "string:" + args.myString)
Log.d("data", "boolean:" + args.myBoolean)
}
}
如果我们在 actionPage1ToPage2(...) 方法里什么都不加的话,就会把在xml设置好的 android:defaultValue 传递过去。
从Page1传递Page2的参数要求 不能为null ,但如果参数类型 支持 null 值 ,那么可以在 <argument> 中,使用 android:defaultValue="@null" 和 app:nullable="true" 结合声明默认值 null。
总的来讲,safe args给我的感觉就是,通过其用来传递数据,就是为了避免接收数据时会报 空指针异常 !
使用 NavigationUI 更新界面组件
导航架构组件包含 NavigationUI 类。此类包含使用顶部应用栏、抽屉式导航栏和底部导航栏管理导航的静态方法。
NavigationUI支持以下类型控件:
- Toolbar
- CollapsingToolbarLayout
- ActionBar
- DrawerLayout
- BottomNavigationView
我们选择 BottomNavigationView 结合 Navigation 做一个示例:
- 在res目录下下新建一个 menu 文件夹。
- 在menu文件夹中创建一个menu资源文件起名为: menu.xml 。
- 在此文件中添加上如下代码:
注意:item中的id必须要与在mobile_navigation.xml中你要显示的Fragment id一致
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="@+id/fragment_page_1_id"
android:icon="@drawable/message"
android:title="Page1" />
<item
android:id="@+id/fragment_page_2_id"
android:icon="@drawable/search"
android:title="Page2" />
<item
android:id="@+id/fragment_page_3_id"
android:icon="@drawable/setting"
android:title="Page3" />
</menu>
然后我们在Activity布局文件中添加 BottomNavigationView 控件,并把刚刚新创建好的 menu 文件 关联 其中。代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
...>
<fragment
android:id="@+id/nav_host_fragment"
... />
<com.google.android.material.bottomnavigation.BottomNavigationView
android:id="@+id/bottom_navigation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:menu="@menu/menu" />
</androidx.constraintlayout.widget.ConstraintLayout>
最后我们在activity的代码中需要先把 NavController 给取出来,再把它传进BottomNavigationView中的 setupWithNavController(...) 方法,而 NavController 是要在 NavHostFragment 里面取,所以第一步就是要把 NavHostFragment找出来先 ,然后取出 NavController ,最后把它传递到 setupWithNavController(...) 方法里就完成Navigation与BottomNavigationView的绑定了。代码如下所示:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
setupBottomNavMenu(navController)
}
private fun setupBottomNavMenu(navController: NavController) {
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNav?.setupWithNavController(navController)
}
}
演示效果如下:
image
但是!看演示的效果会发现,我们依次点击 BottomNavigationView 上面的item,Page2 -> Page3,然后停留在Page3中点击真机上面的 back按键 则会 先跳回到Page1 ,再点一次才退出应用,再试几遍才发现只要你不是停留在Page1中点击back按键都会先返回到Page1中,再点击一次back按键才 退出应用 。
我们要的效果是:只要点击BottomNavigation选择的Fragment,返回栈中只留它一个,再点击back按钮就可退出应用。
image
定位问题:
- 进入 bottomNav?.setupWithNavController(navController) 方法里。
- 发现里面只有一个 NavigationUI.setupWithNavController(this, navController) 方法,并进入该方法。
- 里面有一个对 BottomNavigationView 实现的监听 setOnNavigationItemSelectedListener 里返回了一个 onNavDestinationSelected(item, navController); ,这个方法是用来 关联 Navigation与BottomNavigationView的MenuItem的。
- 问题就是出自这个方法里的 setPopUpTo(int destinationId, boolean inclusive) ,源码如下所示:
public static boolean onNavDestinationSelected(@NonNull MenuItem item,@NonNull NavController navController) {
NavOptions.Builder builder = new NavOptions.Builder()
//...
if ((item.getOrder() & Menu.CATEGORY_SECONDARY) == 0) {
builder.setPopUpTo(findStartDestination(navController.getGraph()).getId(), false);
}
NavOptions options = builder.build();
try {
navController.navigate(item.getItemId(), null, options);
return true;
} catch (IllegalArgumentException e) {
return false;
}
}
当我们每次 点击 BottomNavigationView的 Item 的时候,都会走 setPopUpTo(...) 方法,由于里面只指定“ 初始目的地 ”的id,所以每次都会 弹出 在其之上的目的地,且 inclusive="false" ,这样“初始目的地”在栈中得到 保留 ,并没有被移除!
这就是为什么:只要你不是停留在Page1中点击back按键都会先返回到Page1!
image
解决问题:
- 问题出自一个叫 onNavDestinationSelected(item, navController); 的方法里。
- 该方法里面只实现了导航 跳转目的地 ,与设置跳转时的 进出动画 和目的地的 启动模式 。
- 此方法在BottomNavigationView的 setOnNavigationItemSelectedListener 监听里面。
- 该监听只实现 onNavDestinationSelected(item, navController) 一个方法!
那我们可以试着 重写 BottomNavigationView的 setOnNavigationItemSelectedListener 监听 ,照着 onNavDestinationSelected(...) 方法里面的内容,修改成我们需要的效果不就行了!
activity代码如下所示:
class MainActivity : AppCompatActivity() {
//...
private fun setupBottomNavMenu(navController: NavController) {
val bottomNav = findViewById<BottomNavigationView>(R.id.bottom_navigation)
bottomNav?.setupWithNavController(navController)
//重写监听
bottomNav.setOnNavigationItemSelectedListener { item: MenuItem ->
val options = NavOptions.Builder()
//从放回栈中移除指定目的地
.setPopUpTo(navController.currentDestination!!.id, true)
.setLaunchSingleTop(true)
.build()
try {
//TODO provide proper API instead of using Exceptions as Control-Flow.
navController.navigate(item.itemId, null, options)
true
} catch (e: IllegalArgumentException) {
false
}
}
}
}
演示效果如下:
image
完成!测试结果与我们想要的效果一致!
动态加载Navigation
先把Activity布局文件中的 app:navGraph="@navigation/mobile_navigation" 去掉先。代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
...>
<fragment
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
接着在Activity文件中,通过 navController 将Navigation的xml文件给 inflater 出来,并设置进 navController 的 graph 中。代码如下所示:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
val navGraph: NavGraph =
navController.navInflater.inflate(R.navigation.mobile_navigation)
navController.graph = navGraph
//...
}
//...
}
测试一遍,动态加载成功!
清空返回栈
假如我们打算从Page2跳到Page3时先把返回栈 清空 ,然后跳转到Page3,这时返回栈应该就 只有一个Page3的实例 ,当我们点击back'按键后,直接 退出应用 而不是放回Page2。
这是我在stackoverflow上找到了清空navigation返回栈的方法。
实现代码如下所示:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
...>
<!--
...
-->
<fragment
android:id="@+id/fragment_page_2_id"
...>
<action
android:id="@+id/action_page_2_to_page_3"
app:destination="@id/fragment_page_3_id"
app:launchSingleTop="true"
app:popUpTo="@+id/mobile_navigation"
app:popUpToInclusive="true" />
<!--
...
-->
</fragment>
</navigation>
在Page2跳转到Page3的 <action> 标签下添加上 app:launchSingleTop="true" 、 app:popUpTo="@+id/mobile_navigation" 和 app:popUpToInclusive="true" 就能实现先 清空放回栈然后跳转 。
我们根据xml中添加的这些属性尝试在代码中实现。代码如下所示:
class Page2Fragment : Fragment() {
//...
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
//...
btn_page2_2.setOnClickListener {
val navOption = NavOptions.Builder()
//将xml中三个属性设置进去,就能实现先清栈后跳转
.setLaunchSingleTop(true)
.setPopUpTo(R.id.mobile_navigation, true)
.build()
Navigation.findNavController(it).navigate(R.id.action_page_2_to_page_3, null, navOption)
}
}
}
最后在Activity中实现一个全局清栈功能。
代码很简单,取出 NavController ,然后在调用 navController.navigate(xxx) 方法前调用 navController.popBackStack(R.id.mobile_navigation, true) 方法就能实现清栈。代码如下所示:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val host: NavHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment? ?: return
val navController = host.navController
setupBottomNavMenu(navController)
}
private fun clearStack(navController: NavController) {
navController.popBackStack(R.id.mobile_navigation, true)
}
//...
}
当然“目的地”之间的跳转,应该统一放在activity里面管理,这里就不详细说明了。
只要拿到了navController,就能调用 navController.navigate(xxx) 进行“目的地”跳转。
总结
Android JetPack推出的Navigation架构组件, 用来作为构建应用内界面的框架,其重点是让单Activity应用成为首选架构 。此控件处理了FragmentTransaction 的复杂性,并提供了帮助程序,用于将导航关联到合适的 UI 小部件,例如抽屉式导航栏和底部导航。
image