NavDestination:NavGraph 中的节点,每一个 NavDestination 都代表一个可以被导航到的目的地。
Compose 中由 Destination 来实现,除了 route、arguments、deepLinks 还新增了 @Composable content 属性,用来指定 Destination 对应的 Compose UI。
NavGraph:保存所有 NavDestination 的集合,作为参数传给 NavHost 方法。在 NavHost 方法中保存到 NavController 的 graph 属性中。本身继承自 NavDestination ,使得可以创建出树形结构的嵌套 NavGraph (或者说可以分组创建 NavGraph).
NavController:控制 NavHost 中的导航,在导航时维护 back 栈。只能在 graph 属性中可以已有的 Destination 之间导航。可以通过 graph 属性的 addDestination 方法动态添加 Destination 。
NavHost:导航的容器。 本身在 Compose 树中,用于显示栈顶 Destination 的 content。
- 每一个 NavHost 都有一个 NavController , 每一个 NavController 都有一个 NavGraph 。
- NavHost 存在于 compose 树中用于显示 NavController back 栈顶对象的 @Composable content 。
- 导航时 NavController 检索 NavGraph 从中找到对应的 Destination 并将其添加到 back 栈顶后触发重组更新 NavHost 显示的 @Composable content
NavHost
部分源码( /....../ 代表省略源码),简单了解 NavHost 流程
@Composable
public fun NavHost(navController: NavHostController,graph: NavGraph,
modifier: Modifier = Modifier) {
/*......*/
//将 navController 和 graph 关联起来
navController.graph = graph
/*......*/。
//监听 backStack 和 transitionsInProgress 状态变化
val backStack by composeNavigator.backStack.collectAsState()
val transitionsInProgress by composeNavigator.transitionsInProgress.collectAsState()
/*......*/
//找到即将成为栈顶 或 栈顶的 backStackEntry
val backStackEntry = visibleTransitionsInProgress.lastOrNull() ?: visibleBackStack.lastOrNull()
/*......*/
if (backStackEntry != null) {
/*......*/
//淡入淡出
Crossfade(backStackEntry.id, modifier) {
/*......*/
//显示 content (详情 NavDestination 小节)
(lastEntry.destination as ComposeNavigator.Destination)
.content(lastEntry)
/*......*/
}
}
/*......*/
}
我们在 创建 NavHost 中使用的并不是上面的方法,而是下面的方法。
@Composable
public fun NavHost(
navController: NavHostController,
startDestination: String,
modifier: Modifier = Modifier,
route: String? = null,
builder: NavGraphBuilder.() -> Unit
) {
NavHost(
navController,
//!!!
remember(route, startDestination, builder) {
navController.createGraph(startDestination, route, builder)
},
modifier
)
}
参数上对 NavGraph 的生成做了拆解,还添加了 builder 方便我们在 lambda 中使用 NavGraphBuilder.composable() 来配置 Destination。
NavHost(navController = navController, startDestination = Screen.First.route){
composable("FirstScreen"){
FirstScreen(navController)
}
composable("SecondScreen"){
SecondScreen(navController)
}
}
重点!!! 对于 NavGraph 传参使用了 remember 方法 。
这意味调用这个 NavHost 方法必须一次性将所有 Destination 全部在 builder 中配置好,因为无论 route, startDestination, builder 哪个 key 改变都会触发 remember 的 calculation 生成新的 navGraph ,从而导致 NavHost 重组,新的 navGraph 传入 ,再次执行 navController.graph = graph 。
navController.graph 的重新赋值会导致 navController清空现有的 back 栈,导航到新 navGraph 的 startDestination。
但是 可以通过 navController.graph 的 addDestination 方法动态添加 Destination 。
rememberNavController
- 创建 NavHostController 实例
- 给 NavHostController 的 navigatorProvider 添加 ComposeNavigator 和 DialogNavigator
- 如果参数中传入了 Navigator 也将其添加到 navigatorProvider
- 用 rememberSaveable 的方式保存创建好的 NavHostController 实例并返回
NavHostController 是 NavController 的子类,NavController init 初始化的时候会默认添加 NavGraphNavigator 和 ActivityNavigator。
NavHostController 实例 navigatorProvider 属性包含 NavGraphNavigator 、 ActivityNavigator、ComposeNavigator 和 DialogNavigator 。
Navigator
NavController 导航到 Destination 实际是由 Navigator 完成的。
简单看下 Activity 和 Compose 的 Destination 源码可以看出,不同类型的 Destination 封装的信息的侧重点不同。
Activity 的 Destination 侧重于能匹配到对应 Activity 的信息, Compose 的 Destination 中则包含了对应 UI 的 @composeable content。
框架中 Navigator 是个抽象类,子类有 ActivityNavigator、ComposeNavigator、DialogNavigator、NavGraphNavigator、NoOpNavigator。
除了 NoOpNavigator 只有创建 NavDestination 功能外,其他子类分别负责不同类型 Destination 的导航,并且通过注解指定了 Navigator.Name。
@Navigator.Name("composable")
public class ComposeNavigator : Navigator<Destination>() {
}
NavController navigatorProvider 属性中 Navigator 的种类决定了 NavController 可以处理哪些类型 Destination 的导航。
NavController.navigate()
NavGraph
NavGraph 是保存 NavDestination 的集合提供了增、删、查、迭代 api,
重要属性:
- nodes: SparseArrayCompat
-
- 保存 NavGraph 中所有子节点
- startDestId: Int / startDestIdName: String?
-
- 用于指定 NavGraph 默认启动的 NavDestination
介绍 NavDestination 时,重点标记过 NavGraph 也是一种 NavDestination,而且 NavController init 时也会给 navigatorProvider 添加 NavGraphNavigator 。
如果要导航到的 NavDestination 是 NavGraph 类型, NavGraphNavigator 会将导航到 startDestId/startDestIdName 属性对应的 NavDestination。(详细介绍放在 navigation 流程 )
这样也使得 NavGraph 可以分组配置,类似 ViewGroup - View 的嵌套结构。
- 使用 navController 中的 navigatorProvider 和 传入的参数 创建 NavGraphBuilder 对象
- apply(builder) 执行传入的 builder , builder 中的 composable 被执行 将 DSL 转换成 ComposeNavigator.Destination 添加到 NavGraphBuilder 的 destinations 属性中
- build() 创建 NavGraph 对象 ,将 NavGraphBuilder 中的属性设置给 NavGraph
使用时我们并没有直接创建 NavGraph ,而是将 NavGraphBuilder 作为参数传递给 HavHost() 方法。
NavGraph 创建是在 NavHost() 方法中完成的
NavGraphBuilder
创建 NavGraph 的 DSL, 继承 NavDestinationBuilder 。 重要属性:
- provider: NavigatorProvider
-
- 用于创建 NavDestination
- startDestinationId: Int / startDestinationRoute: String
-
- 对应 NavGraph 的 startDestId/startDestIdName
- destinations: MutableList
-
- 对应 NavGraph 的 nodes
NavGraphBuilder.build
查看 NavDestinationBuilder 源码会发现 NavDestinationBuilder 是用来创建单个 NavDestination 的,而 NavGraphBuilder 是用来创建整个 NavGraph(1 个 NavGraph 对象 和 0~n 个 NavDestination 节点) 的。
build 方法是如何完成构建逻辑的呢?
用 super.builder() 创建一个 NavGraph 对象(NavGraphBuilder 声明的时父类泛型是 NavGraph ),再将自己的属性设置给 NavGraph 对象。
NavGraphBuilder.composable
使用 NavGraphBuilder 时我们通常配合 composable DSL 一起。
composable DSL 执行时会将所有参数封装成 ComposeNavigator.Destination 并调用 NavGraphBuilder.addDestination() 方法将其添加到 destinations 属性中。
简单解析只涉及了使用时必须知道的类和流程,完整的 Navigation-Compose 组件要复杂的多。