Jetpack Compose 的 Navigation学习

Posted RikkaTheWorld

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Jetpack Compose 的 Navigation学习相关的知识,希望对你有一定的参考价值。

文章目录

1. 概述

Navigation 是 Jetpack 用于 android 导航的组件,作用是处理页面跳转,以及页面跳转过程中的交互。

使用 Navigation,你就需要为每个页面设定一条唯一路径,它是一个 String 常量,形式是 DeepLink 的样子,从一个页面跳转到另一个页面,它通过输入目的地的路径进行转跳。

Navigation 也支持上 Compose,我们可以在 Compose 中使用它来进行跳转。

因为官网和所看到的demo都是把它作为跳转能力使用,所以不清楚 Compose 是否还有别的跳转方式…

2. Navgation api

2.1 使用 NavHost

NavHost 可以用来描述导航图,就是可以在上面指定那些可以跳转的页面(像是注册路由一样),如果你需要从一个页面跳到另一个页面,你需要改变路线, NavHost 会触发重组,然后跳转到目的地。

NavHost 的重组是由 NavController 触发的,它代表了 NavHost 的状态, 是 Navigation 在 Compose 的中心 api, 它有两个作用:

  • 跟踪界面上可组合项的回退栈
  • 跟踪界面上每个可组合项的状态

我们通过下面方式创建它:

val navController = rememberNavController()

要使用 NavHost, 必须要绑定一个 NavController,并且通过 startDestination 设置一个默认起始页面:

NavHost(navController = navController, startDestination = "profile") 
    composable("profile")  Profile(/*...*/) 
    composable("friendslist")  FriendsList(/*...*/) 
    /*...*/

2.2 composable()

上面在 NavHost 中填入的 composable 是什么呢?

composable(..) ... 是 Navigation 提供的 DSL,它可以让我们添加可导航的页面,一个 composable() 可以视为注册一个可导航页面,就像下面这样:

// 为 Profile 赋予导航能力,相当于注册到路由表上
composable(
    "profile?userId=userId",  // 1
    arguments = listOf(navArgument("userId")  defaultValue = "me" ) // 2
)  backStackEntry ->  // 3
    Profile(navController, backStackEntry.arguments?.getString("userId"))

它一般有三个参数,下面逐一解释。

2.2.1 注释1:id

表示页面的路径,是页面的导航标识,是 String 类型。

可以通过 ...设置占位符,例如例子里面的 userId。

还可以扩展其能力,做为 deeplink,例如跳转到 web 页,具体将在下面讲解。

2.2.2 注释2: arguments

导航目的地的可组合项所需参数,是可选参数, 需要使用 NavArgument 组成的列表携带。

默认情况下,所有参数都会作为字符串被解析, 我们可以通过 type设置。

NavHost(startDestination = "profile/userId/age") 
    ...
    composable(
        "profile/userId/age",
        arguments = listOf(
            navArgument("userId")  type = NavType.StringType , // 这里可以不用写,因为默认就是字符串
            navArgument("age")  type = NavType.IntType   // 需要显式设置为 Int 类型
        )
    ) ...

我们还可以为 navArgument 设置别的可选参数,例如 defaultValuenullability = true(默认值可空):

navArgument("userId")  default = "123456" 

2.2.3 注释3:lambda表达式

它是一个用于构造一个可组合项,表示页面内容。看起来是一个 (NavBackStackEntry) -> @Compsable() 类型。

NavBackStackEntry 表示回退栈信息, 它的 argument 携带了打开页面所需要的参数,我们可以使用它来提取参数,然后构造可组合项:

composable("profile/userId")  backStackEntry ->
    Profile(navController, backStackEntry.arguments?.getString("userId"))

2.3 页面跳转

通过调用 navController.navgate(..) 进行导航, 参数是目的地的标识,如下所示:

@Composable
fun Profile(navController: NavController) 
    /*...*/
    Button(onClick =  navController.navigate("friendslist") ) 
        Text(text = "Navigate next")
    
    /*...*/

需要注意的是,我们需要在回调中才能使用它,避免在可组合项中直接使用,避免重组时调用了它。

如果我们需要携带参数,可以直接填入:

navController.navigate("profile/user1234")
navController.navigate("profile/$getUid()")

默认情况下, navigate 会将新目的地添加到回退栈中,我们可以通过添加别的参数来改变这个行为:

// 在进入"friendsList"之前,回退栈会弹出所有的可组合项,直到 "home"
navController.navigate("friendslist") 
    popUpTo("home")


// 在进入"friendsList"之前,回退栈会弹出所有的可组合项,直到 "home",并且包括它
navController.navigate("friendslist") 
    popUpTo("home")  inclusive = true 


// 对应 Android 的 SingleTop,如果回退栈顶部已经是 "search",就不会重新创建
navController.navigate("search") 
    launchSingleTop = true

更多能力请参考: popUpTo 指南

2.4 DeepLink

Navigation Compose 支持隐式深层链接,此类链接也可定义为 composable() 函数的一部分。使用 navDeepLink() 以列表的形式添加深层链接。

例如下面代码中定义的导航项,它将页面参数拼接到 deepLink:

val uri = "https://www.example.com"

composable(
    "profile?id=id",  
    deepLinks = listOf(navDeepLink  uriPattern = "$uri/id" )
)  backStackEntry ->
    Profile(navController, backStackEntry.arguments?.getString("id"))

借助这些深层链接,可以将特定的网址、操作和/或 MIME 类型与可组合项关联起来。

我们可以在 manifest.xml 文件添加相应的 <intent-filter> 元素,使得外部可以访问。如需启用上述深层链接,可以在清单的 <activity> 元素中添加以下内容:

<activity …>
  <intent-filter>
    ...
    <data android:scheme="https" android:host="www.example.com" />
  </intent-filter>
</activity>

当其他应用触发该深层链接时,Navigation 会自动深层链接到相应的可组合项。

这些深层链接还可用于构建包含可组合项中的相关深层链接的 PendingIntent

val id = "exampleId"
val context = LocalContext.current
val deepLinkIntent = Intent(
    Intent.ACTION_VIEW,
    "https://www.example.com/$id".toUri(),
    context,
    MyActivity::class.java
)

val deepLinkPendingIntent: PendingIntent? = TaskStackBuilder.create(context).run 
    addNextIntentWithParentStack(deepLinkIntent)
    getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT)

2.5 嵌套结构

一个 NavHost 内注册的可组合项看起来是平铺的,没有深层结构。 我们可以通过 navigation 函数为其增加嵌套内容。

例如下面定义一个导航结构, “login” 可组合项又可以导航到 “username”、“password”、“registration” 这三个可组合项,而不让别的与 login 同级的可组合项进入,我们可以这样写:

NavHost(navController, startDestination = "home") 
    ...
    // 通过 route 来为'login' 定义一个更深层次的导航图
    // 因此'username'封装了图的内部路由逻辑
    navigation(route = "login", startDestination = "username") 
        composable("username")  ... 
        composable("password")  ... 
        composable("registration")  ... 
    
    ...

官方建议,如果在导航图比较大时,将其作为扩展函数拆分一下,以调高代码可读性:

fun NavGraphBuilder.loginGraph(navController: NavController) 
    navigation(startDestination = "username", route = "login") 
        composable("username")  ... 
        composable("password")  ... 
        composable("registration")  ... 
    


// 使用:
NavHost(navController, startDestination = "home") 
    ...
    loginGraph(navController)
    ...

官网

使用 Compose 进行导航

以上是关于Jetpack Compose 的 Navigation学习的主要内容,如果未能解决你的问题,请参考以下文章

Android Jetpack Compose学习—— Jetpack compose基础布局

Android Jetpack Compose学习—— Jetpack compose基础布局

Android Jetpack Compose学习—— Jetpack compose入门

Android Jetpack Compose学习—— Jetpack compose入门

jetpack compose 接收返回参数

什么是Jetpack Compose?带你走进Jetpack Compose~