想要在APP中使用Jetpack的Navigation组件进行页面跳转,需要事先为其配置NavGraph
基于XML配置
通常可以使用xml进行配置,例如官方sunflower
例子中的nav_garden.xml
<!-- nav_garden.xml -->
<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"
app:startDestination="@id/view_pager_fragment">
<fragment
android:id="@+id/view_pager_fragment"
android:name="com.google.samples.apps.sunflower.HomeViewPagerFragment"
tools:layout="@layout/fragment_view_pager">
<action
android:id="@+id/action_view_pager_fragment_to_plant_detail_fragment"
app:destination="@id/plant_detail_fragment"
app:enterAnim="@anim/slide_in_right"
app:exitAnim="@anim/slide_out_left"
app:popEnterAnim="@anim/slide_in_left"
app:popExitAnim="@anim/slide_out_right" />
</fragment>
<fragment
android:id="@+id/plant_detail_fragment"
android:name="com.google.samples.apps.sunflower.PlantDetailFragment"
android:label="@string/plant_details_title"
tools:layout="@layout/fragment_plant_detail">
<action
android:id="@+id/action_plant_detail_fragment_to_gallery_fragment"
app:destination="@id/gallery_fragment"
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="plantId"
app:argType="string" />
</fragment>
<fragment
android:id="@+id/gallery_fragment"
android:name="com.google.samples.apps.sunflower.GalleryFragment"
android:label="@string/plant_details_title"
tools:layout="@layout/fragment_gallery">
<argument
android:name="plantName"
app:argType="string" />
</fragment>
</navigation>
然后在Layout的根Fragment(即NavHostFragment
)配置此xml资源
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_garden" /> <!--设置xml资源-->
</layout>
基于DSL配置
随着Kotlin在Android中的日益普及,使用Kotlin的dsl替代xml或许是一个更好的选择。谷歌官方顺势为Navigation推出了KTX
扩展,支持使用dsl配置NavGraph
dependencies {
def nav_version = "2.3.1"
implementation "androidx.navigation:navigation-fragment-ktx:$nav_version"
}
使用dsl后,NavHostFragment无需再指定xml资源
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
- app:defaultNavHost="true"
- app:navGraph="@navigation/nav_garden"
+ app:defaultNavHost="true" />
然后我们直接在代码中通过dsl配置NavGraph。
//GardenActivity.kt
class GardenActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_garden)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host) as NavHostFragment
navHostFragment.navController.apply {
graph = createGraph(nav_graph.id, nav_graph.dest.home) { // this: NavGraphBuilder
// 配置Fragment Destination
fragment<HomeViewPagerFragment>(nav_graph.dest.home) {
label = getString(R.string.home_title)
action(nav_graph.action.to_plant_detail) {
destinationId = nav_graph.dest.plant_detail
}
}
fragment<PlantDetailFragment>(nav_graph.dest.plant_detail) {
label = getString(R.string.plant_detail_title)
argument(nav_graph.args.plant_id) {
type = NavType.StringType
}
}
}
}
}
}
createGraph()
创建并返回NavGraph,fragment<T>()
用来在graph中配置Fragment Destination,通过泛型T可以更加TypeSafe的指定Fragment类型。action
、argument
等用来配置fragment相关信息。不输于XML的可读性,同时避免了解析XML的耗时。
action
、argument
等所需的IdRes
,需要通过常量进行配置
//NavGraph.kt
object nav_graph {
const val id = 1 // graph id
object dest {
const val home = 2
const val plant_detail = 3
}
object action {
const val to_plant_detail = 4
}
object args {
const val plant_id = "plantId"
}
}
使用效果
使用dsl配置的Navigation进行页面跳转与传统的xml方式几乎没有区别,只是需要注意将IdRes
替换为Kt常量
即可:
//GardenPlantingAdapter.kt
private fun navigateToPlant(plantId: String) {
val args = bundleOf(nav_graph.args.plant_id to plantId)
findNavController().navigate(nav_graph.action.to_plant_detail, args)
}
dsl也同样支持Deeplinks
配置
//GardenActivity.kt
fragment<PlantDetailFragment>(nav_graph.dest.plant_detail) {
label = getString(R.string.plant_details_title)
argument(nav_graph.args.plant_id) {
type = NavType.StringType
}
deepLink("https://www.example.com/plants/{plantId}/") //deeplink配置
// deepLink(getString(R.string.deep_link_plants)) 也可以使用StringRes配置
}
需要特别注意的是,Deeplinks功能需要在AndroidManifest.xml
中对Activity进行配置,传统的xml方式在<Activity/>
下添加<nav-graph>
即可:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.myapplication">
<application ... >
<activity name=".MainActivity" ...>
...
<nav-graph android:value="@navigation/nav_graph" />
...
</activity>
</application>
</manifest>
dsl方式因为不依赖xml资源,所以需要针对Intent
手动配置Deeplink所需的信息(即url等):
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.BROWSABLE" />
<category android:name="android.intent.category.DEFAULT" />
<data
android:host="www.example.com"
android:pathPattern="/plants.*"
android:scheme="https" />
</intent-filter>