【Android Jetpack】使用Kotlin DSL配置Navigation Components

在这里插入图片描述
想要在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类型。actionargument等用来配置fragment相关信息。不输于XML的可读性,同时避免了解析XML的耗时。

actionargument等所需的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>

猜你喜欢

转载自blog.csdn.net/vitaviva/article/details/110679791