jetpack组件---导航框架:navigation
什么是navigation?
谷歌官方:
Navigation 是一个框架,用于在 Android 应用中的“目标”之间导航,该框架提供一致的 API,无论目标是作为 Fragment、Activity 还是其他组件实现。
navigation就是一套起初主要用于管理fragment之间跳转的框架,后面逐渐也被大家应用到activity间的跳转,说到activity之间的跳转,方式已经很多了,比如最原始的Intent跳转,然后到比较好用的Arouter,特别是Arouter,保证了每个activity的独立性,有一定的解耦功能,我觉得比使用navigation来管理activity跳转更好用,所以我们可以人为navigation就是用来管理fragment的跳转的吧。
使用前的准备
如需添加 Navigation 的依赖项,您必须将 Google Maven 代码库添加到项目中。如需了解详情,请参阅 Google 的 Maven 代码库。
在应用或模块的 build.gradle
文件中添加所需工件的依赖项:
dependencies {
val nav_version = "2.3.5"
// Java language implementation
implementation("androidx.navigation:navigation-fragment:$nav_version")
implementation("androidx.navigation:navigation-ui:$nav_version")
// Kotlin
implementation("androidx.navigation:navigation-fragment-ktx:$nav_version")
implementation("androidx.navigation:navigation-ui-ktx:$nav_version")
// Feature module Support
implementation("androidx.navigation:navigation-dynamic-features-fragment:$nav_version")
// Testing Navigation
androidTestImplementation("androidx.navigation:navigation-testing:$nav_version")
// Jetpack Compose Integration
implementation("androidx.navigation:navigation-compose:")
}
上面是谷歌给出的声明依赖项的标准,总的需要添加那么多依赖,当然如果你是和我一样使用kotlin开发的,可以选择性添加,比如我的是这样的:
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
def nav_version = "1.0.0-alpha05"
implementation "android.arch.navigation:navigation-fragment:$nav_version"
implementation "android.arch.navigation:navigation-ui:$nav_version"
androidTestImplementation "android.arch.navigation:navigation-testing:$nav_version"
接下来就是创建资源文件,创建方法:在 res 目录右击,选择 New > Android Resource File,Resource type 选择 Navigation。如下图:


可能你在创建的过程中会出现Resource type 找不到navigation选项的问题,那是因为在 Android Studio 3.2 Canary 14 以上的版本中,需要打开 Preferences -> Experimental -> Enable Navigation Editor ,然后重启Android studio
一些其他的准备
如需将 Safe Args 添加到您的项目,请在顶层 build.gradle
文件中包含以下 classpath
:
buildscript {
repositories {
google()
}
dependencies {
val nav_version = "2.3.5"
classpath("androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version")
}
}
您还必须应用以下两个可用插件之一。
如需生成适用于 Java 模块或 Java 和 Kotlin 混合模块的 Java 语言代码,请将以下行添加到应用或模块的 build.gradle 文件中:
plugins {
id("androidx.navigation.safeargs")
}
此外,如需生成适用于仅 Kotlin 模块的 Kotlin 语言代码,请添加以下行:
plugins {
id("androidx.navigation.safeargs.kotlin")
}
上面这些其他的准备是官方给出的,说的非常模糊,那具体是干什么的呢?其实就是安全性传递参数需要做的准备,后面说到参数传递的时候会再次提到。
Navigation初使用
我们先把navigation用起来,比如现在我们先实现一个activity里面包含两个fragment,让这两个fragment之间可以相互跳转。
我们先创建一个新的项目navigationDemo;
然后创建第一个fragment:
class FirstFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.first_fragment, container, false)
}
}
first_fragment.xml
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="這是第一個fragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:text="跳轉"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
接着继续创建第二个fragment:
class SecondFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_nav_second_fragment, container, false)
}
}
second_fragment.xml:
<?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=".SecondFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是第二个fragment"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/backFirst"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="15dp"
android:text="回去"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
这两个fragment创建好以后,我们就可以做些navigation的准备工作了,也就是上文所提到的准备工作:
创建资源文件,创建方法:在 res 目录右击,选择 New > Android Resource File,Resource type 选择 Navigation。取名nav_graph
它会在res下的navigation下生成一个nav_graph.xml的文件,文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/nav_graph"
>
</navigation>
这个文件里面放的是navigation的导航图,我们在里面添加创建的第一个fragment:
<?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/nav_first_fragment"
>
<fragment
android:id="@+id/nav_first_fragment"
android:name="com.example.navicationdemo.FirstFragment"
android:label="first"
tools:layout="@layout/first_fragment">
</fragment>
</navigation>
参数说明:
android:id 任意命名,在fragment之间创建action时需要用到
android:name 关联的fragment类全名,这里是我们创建的第一个fragment的类全名
android:label 辨识标签,无实际意义,可随意命名
tools:layout 指定该fragment相应的布局文件
app:startDestination 指定第一个fragment
接下来一样的将第二个fragment也添加进去:
<?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"
>
<fragment
android:id="@+id/nav_first_fragment"
android:name="com.example.navicationdemo.FirstFragment"
android:label="first"
tools:layout="@layout/first_fragment">
</fragment>
<fragment
android:id="@+id/nav_second_fragment"
android:name="com.example.navicationdemo.SecondFragment"
android:label="second"
tools:layout="@layout/second_fragment">
</fragment>
</navigation>
这样我们就已经往导航视图里面添加了两个fragment,导航图右边显示如下:

可以看到我们只是单纯地将他添加进去,两者之间并无关联,我们的初衷是希望实现他们之间点击按钮分别跳转,所以需要在nav_graph.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/nav_graph"
>
<fragment
android:id="@+id/nav_first_fragment"
android:name="com.example.navicationdemo.FirstFragment"
android:label="first"
tools:layout="@layout/first_fragment">
<action
android:id="@+id/firstfragment_to_secondfragment"
app:destination="@+id/nav_second_fragment" />
>
</fragment>
<fragment
android:id="@+id/nav_second_fragment"
android:name="com.example.navicationdemo.SecondFragment"
android:label="second"
tools:layout="@layout/second_fragment">
<action
android:id="@+id/secondfragment_to_firstfragment"
app:destination="@+id/nav_first_fragment" />
</fragment>
</navigation>
action里面的属性意义:
android:id 该action的id,做跳转操作指定action时使用它作为标识
app:destination 指定该action执行时需要跳转到的fragment的id
然后右边两个fragment之间也有了联系:

我们看到有两个箭头分别指向对方,这就是跳转的action。
下面就是在fragment里面实现按钮点击事件
FirstFragment:
class FirstFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.first_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button1.setOnClickListener {
Navigation.findNavController(getView()!!)
.navigate(R.id.firstfragment_to_secondfragment)
}
}
}
跳转时调用 NavController 的 navigate 方法执行跳转,navigate 的参数可以是一个 fragment 在导航图 nav_graph 中的 id,也可以是 action 的 id。我们这里使用的是action的id。
SecondFragment也是一样的方法:
class SecondFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.second_fragment, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
backFirst.setOnClickListener {
Navigation.findNavController(getView()!!)
.navigate(R.id.secondfragment_to_firstfragment)
}
}
}
这样咱们的基本跳转就实现了,可以实现跳转,但在实际中为了让跳转更加明显,通常需要加入跳转时的动画效果。跳转动画应该在action里面实现,你可以在action里面使用代码实现,也可以使用design直接实现,
我们为了快捷就用第二种方法,具体做法:切换到nav_graph.xml,点击右上角Design,点击视图上的箭头(其实箭头就是代表action)

右边四种动画效果选择如下:

这样就实现了跳转的动画效果。
最后把它放到activity里面,这里有两种实现方式,第一种是直接在activity的xml中应用:
<?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_firstfragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
第二种方式是通过代码创建 NavHostFragment:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
... >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/frame_layout" />
</android.support.constraint.ConstraintLayout>
MainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_navigation)
val finalHost = NavHostFragment.create(R.navigation.nav_graph)
supportFragmentManager.beginTransaction()
.replace(R.id.frame_layout, finalHost)
.setPrimaryNavigationFragment(finalHost) // 等价于 xml 中的 app:defaultNavHost="true"
.commit()
}
最后运行项目,点击按钮就可以实现跳转了。
数据的传递
比如第一个fragment跳转第二个fragment时携带参数,先在nav_graph.xml的第二个fragment里添加argument
<fragment
android:id="@+id/nav_second_fragment"
android:name="com.example.navicationdemo.SecondFragment"
android:label="second"
tools:layout="@layout/second_fragment">
<argument
android:name="name"
android:defaultValue="zzp" />
<action
android:id="@+id/secondfragment_to_firstfragment"
app:destination="@+id/nav_first_fragment"
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>
参数说明:argument的name类似于键值对里的key值,defaultvalue就是接收参数失败时的数据。
FirstFragment 添加数据
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
button1.setOnClickListener {
val bundle= bundleOf("name" to "路飞")
Navigation.findNavController(getView()!!)
.navigate(R.id.firstfragment_to_secondfragment,bundle)
}
}
SecondFargment
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d("zzp","你好 ${arguments?.getString("name")}")
return inflater.inflate(R.layout.second_fragment, container, false)
}
运行效果:

参看文档:
Navigation 详解一 (大神写的,写的很牛,建议大家去看看他的这篇文章)
谷歌官方简陋说明
Navigation官方API