Jetpack提供了一个名为Navigation的组件,用来管理页面(Actvity和Fragment,以Fragment为主)和App bar。
Navigation的优势:
- 可视化的页面导航图,便于理清页面间的关系
- 通过destination和action完成页面间的导航
- 方便添加页面切换动画
- 页面间类型安全的参数传递
- 通过NavigationUI类,对菜单、底部导航、抽屉菜单导航进行统一的管理
- 支持深层链接DeepLink
Navigation的主要元素
- Navigation Graph。一种新型的XML资源文件,其中包含应用程序所有的页面,以及页面间的关系。
- NavHostFragment。这是一个特殊的Fragment,是其他Fragment的“容器”,Navigation Graph中的Fragment正是通过NavHostFragment进行展示的。
- NavController。这个是一个Java/kotlin对象,用于在代码中完成Navigation Graph中具体的页面切换工作。
使用方法
- 添加依赖
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.0'
- 创建Navigation Graph
在res文件中新建一个Navigation Graph文件,将File name设置为“nav_graph”,Resource Type设置为“Navigation”。
- 添加NavHostFragment
在Activity的布局文件中添加NavHostFragment
<fragment
android:id="@+id/nav_host_fragment_content_main"
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/nav_graph" />
android:name="androidx.navigation.fragment.NavHostFragment"
表明这是一个特殊的Fragment
app:defaultNavHost="true"
该Fragment会自动处理系统返回键
app:navGraph="@navigation/nav_graph"
设置该Fragment对应的导航图
- 创建destination
<?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/FirstFragment">
<fragment
android:id="@+id/FirstFragment"
android:name="com.example.myapplication.FirstFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_first">
<action
android:id="@+id/action_FirstFragment_to_SecondFragment"
app:destination="@id/SecondFragment" />
</fragment>
<fragment
android:id="@+id/SecondFragment"
android:name="com.example.myapplication.SecondFragment"
android:label="@string/second_fragment_label"
tools:layout="@layout/fragment_second">
<action
android:id="@+id/action_SecondFragment_to_FirstFragment"
app:destination="@id/FirstFragment" />
</fragment>
</navigation>
app:startDestination="@id/FirstFragment"
表明FirstFragment为起始Fragment,即NavHostFragment容器首先展示的Fragment
app:destination="@id/SecondFragment"
表示action动作的目的地是SecondFragment
- 使用NavController完成导航
添加FirstFragment的布局文件
<?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=".FirstFragment">
<TextView
android:id="@+id/textview_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/hello_first_fragment"
app:layout_constraintBottom_toTopOf="@id/button_first"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/button_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textview_first" />
</androidx.constraintlayout.widget.ConstraintLayout>
在Fragment中添加Button点击的监听
binding.buttonFirst.setOnClickListener {
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
}
- 添加页面切换动画效果
在res/anim文件中添加动画文件slide_in_left.xml,slide_in_right.xml,slide_out_left.xml,slide_out_right.xml
然后修改nav_graph文件的action
<action
android:id="@+id/action_FirstFragment_to_SecondFragment"
app:destination="@id/SecondFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
使用safe args插件传递参数
- 在Project的build.gradle中添加依赖
dependencies {
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.2"
}
在app的build.gradle中添加依赖
plugins {
id 'androidx.navigation.safeargs'
}
- 在nav_graph中添加<argument>标签
<fragment
android:id="@+id/FirstFragment"
android:name="com.example.myapplication.FirstFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_first">
<action
android:id="@+id/action_FirstFragment_to_SecondFragment"
app:destination="@id/SecondFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<argument
android:name="user_name"
app:argType="string"
android:defaultValue="unknown"/>
<argument
android:name="age"
app:argType="integer"
android:defaultValue="0"/>
</fragment>
- 在FirstFragment中传递参数
val bundle = FirstFragmentArgs.Builder().setUserName("Name").setAge(20).build().toBundle()
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment, bundle)
- 在SecondFragment中接收参数
arguments?.let {
val userName = FirstFragmentArgs.fromBundle(it).userName
val age = FirstFragmentArgs.fromBundle(it).age
binding.textviewSecond.text = "$userName $age"
}
NavigationUI的使用方法
Navigation和App bar都需要处理页面切换事件,为了方便管理,Jetpack引入了NavigationUI组件,使App bar中的按钮和菜单能够与Navigation Graph中的页面关联起来。
- 在res/menu中创建menu_main.xml文件,为ActionBar添加菜单。注意<itme/>的id与导航图中fragment的id是一致的。
<menu 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"
tools:context="com.example.myapplication.MainActivity">
<item
android:id="@+id/FirstFragment"
android:title="@string/first_fragment_label"
app:showAsAction="never" />
<item
android:id="@+id/SecondFragment"
android:title="@string/second_fragment_label"
app:showAsAction="never" />
</menu>
- 在Activity中实例化菜单。
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
- 绑定Navigation和ActionBar
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var binding: ActivityMainBinding
private lateinit var navController: NavController
private val myLifecycleObserver = MyLifecycleObserver(MainActivity::class.java.simpleName)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
setSupportActionBar(binding.toolbar)
navController = findNavController(R.id.nav_host_fragment_content_main)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return NavigationUI.onNavDestinationSelected(item, navController) || super.onOptionsItemSelected(item)
}
override fun onSupportNavigateUp(): Boolean {
return navController.navigateUp(appBarConfiguration)
|| super.onSupportNavigateUp()
}
AppBarConfiguration用于App bar的配置,NavController用于页面的导航和切换,通过setupActionBarWithNavController方法将二者绑定起来。
同时Jetpack提供了对页面切换事件的监听。
navController.addOnDestinationChangedListener { controller, destination, arguments -> TODO("Not yet implemented") }
深层链接DeepLink
PendingIntent
当应用程序接收到某个通知推送,用户在单击该通知时,能够直接跳转到展示通知内容的页面。
获取创建通知的PendingIntent
private fun getPendingIntent(): PendingIntent {
val bundle = Bundle()
bundle.putString("params", "Notification Params")
return navController.createDeepLink().setGraph(R.navigation.nav_graph)
.setDestination(R.id.SecondFragment).setArguments(bundle).createPendingIntent()
}
URL
- 在nav_graph中添加<deepLink/>标签
<fragment
android:id="@+id/FirstFragment"
android:name="com.example.myapplication.FirstFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_first">
<action
android:id="@+id/action_FirstFragment_to_SecondFragment"
app:destination="@id/SecondFragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
<argument
android:name="user_name"
app:argType="string"
android:defaultValue="unknown"/>
<argument
android:name="age"
app:argType="integer"
android:defaultValue="0"/>
<deepLink app:uri="www.YourAddress.com/{params}"/>
</fragment>
- 在AndroidManifest中为Activity添加<nav-graph/>标签
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:theme="@style/Theme.MyApplication.NoActionBar">
<nav-graph android:value="@navigation/nav_graph"/>
</intent-filter>
</activity>