Compose | UI组件(十三) | Navigation - 页面导航

本文详细介绍了JetpackCompose中的Navigation组件,包括核心概念如NavController、NavHost、Screen、Route等,以及如何通过NavGraph管理页面跳转和传参,展示了从新建页面到实际操作的完整流程和各种导航模式的示例。
摘要由CSDN通过智能技术生成


前言

关于大前端,不管是Android还是IOS,Web端都会涉及到页面之间的跳转或传值

今天我们要讲的就是Compose中及其重要的组件 Navigation (页面导航)


Navaigation 的核心概念和组件的更详细说明

Compose Navigation 是一个用于 Jetpack Compose 的导航组件,它提供了简单而强大的 API 来处理页面跳转和用户界面交互。以下是 Compose Navigation 的核心概念和组件的更详细说明:

  1. NavController
    • 定义:NavController 是导航组件的中心 API,负责控制页面导航的流程。
    • 作用:它维护了 Navigation 内部关于页面的堆栈、状态信息、导航图。
    • 创建:通过 rememberNavController() 方法创建 NavController 实例。
  2. NavHost
    • 定义:NavHost 是一个 Composable 函数,用于承载导航的页面,同时也是承载导航页面的容器。
    • 参数:NavHost 需要两个必传参数,一个是 NavController,一个是起始路由地址。
    • 作用:内部持有 NavController,在页面切换时渲染 UI。通过 composable() 函数构建路线(节点)。
  3. Screen
    • 定义:Screen 是表示一个单独的、完整的 UI 屏幕的组件。可以将其视为一个页面,包含了一个完整的 UI 布局。
    • 创建:通过 Screen 组件创建 Screen 实例。
  4. Route
    • 定义:Route 是导航路径的抽象表示,用于定义页面之间的跳转关系。
    • 创建与管理:通过 NavHost 和 NavController 定义和管理多个 Route。
  5. Params
    • 定义:Params 是传递给目标 Screen 的数据。
    • 传递方式:通过 NavController 的 navigate() 方法传递 Params。在目标 Screen 中接收和使用这些参数。
  6. Backstack
    • 定义:Backstack 是保存和恢复导航状态的机制。
    • 管理方式:通过 NavController 的 backstack 属性,可以管理用户的导航历史,并执行后退操作。
  7. Transitions
    • 定义:Transitions 是用于页面切换的动画效果。
    • 提供与自定义:Compose Navigation 提供了一组默认的过渡动画,也可以自定义过渡效果,以提供流畅的用户体验。
  8. Lazy Entry
    • 定义:Lazy Entry 是一种懒加载技术,允许延迟加载一些不立即需要的页面。
    • 用途:优化性能和资源使用。
  9. Graph:
    • 定义:统一所有的Destination信息,以及可能的跳转路径
    • 用途:Navigation需要收集各节点之间的跳转关系,因此NavDestinotion需要集中在一起,统一由Graph管理
  10. NavLink:类似于 Web 中的锚点,可以用来实现页面内的跳转。

这些核心概念和组件为开发者提供了构建复杂的导航结构和用户界面交互的工具,使得在 Jetpack Compose 应用中实现高效的页面跳转和数据传递变得简单而强大。通过合理使用这些概念和组件,可以创建出具有良好用户体验的应用程序。


真实案例带你一步一步了解 Navaigation

第一步,新建多个页面

多个页面相当于传统View里面的Fragment

HomeScreen.kt

@Composable
fun HomeScreen(navController: NavController){
Column(modifier = Modifier.fillMaxSize(),
       verticalArrangement = Arrangement.Center,
       horizontalAlignment = Alignment.CenterHorizontally) {

        Text(modifier = Modifier.clickable {},
            text  = "Home",
            color = MaterialTheme.colorScheme.primary,
            style = MaterialTheme.typography.displayLarge)

        Text(modifier = Modifier.padding(top = 150.dp).clickable {},
            text  = "Login/SinUp",
            color = Color.Black,
            style = MaterialTheme.typography.headlineMedium)
   }
}

LoginScreen.kt

@Composable
fun LoginScreen(navController: NavController){
    Column(modifier = Modifier.fillMaxSize(),
        verticalArrangement = Arrangement.Center,
        horizontalAlignment = Alignment.CenterHorizontally) {

        Text(modifier = Modifier.clickable {},
            text  = "Login",
            color = Color.Magenta,
            style = MaterialTheme.typography.displayLarge)

        Text(modifier = Modifier.padding(top = 150.dp).clickable {},
            text  = "Go Back",
            color = Color.Black,
            style = MaterialTheme.typography.headlineMedium)
    }
}

SignUpScreen.kt

@Composable
fun SignUpScreen(navController: NavController){
    Box(modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center) {

        Text(modifier = Modifier.clickable {},
            text  = "SingUp",
            color = Color.Green,
            style = MaterialTheme.typography.displayLarge)
    }
}

DetailScreen.kt

@Composable
fun DetailScreen(navController: NavController){
    Box(modifier = Modifier.fillMaxSize(),
        contentAlignment = Alignment.Center) {

        Text(modifier = Modifier.clickable {},
            text  = "Detail",
            color = Color.Red,
            style = MaterialTheme.typography.displayLarge)
    }
}

第二步,新建一个Activity

class NavigationActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            ComposeProjectTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    SetupNavGraph(rememberNavController())
                }
            }
        }
    }
}

注:在Activity中写了一个SetupNavGraph方法,该方法就是设置导航的统一管理方法

第三步,新建一个Screen类,用于统一管理导航的常量

Screen.kt

const val AUTHENTICATION_ROUTE = "authentication"
const val ROOT_ROUTE = "root"
const val HOME_ROUTE = "home"

const val HOME   = "home_screen"
const val LOGIN  = "login_screen"
const val SIGNUP = "signup_screen"
const val DETAIL = "detail_screen"

sealed class Screen(val route:String){
    object Home:Screen(route = HOME)

    object Login:Screen(route = LOGIN)

    object Signup:Screen(route = SIGNUP)

    object Detail:Screen(route = DETAIL)
}

第四步,新建一个Graph 统一管理方法

NavGraph.kt

@Composable
fun SetupNavGraph(navController: NavHostController){
    NavHost(navController = navController, startDestination = Screen.Home.route){
         composable(route = Screen.Home.route){
             HomeScreen(navController   = navController)
         }
         composable(route = Screen.Detail.route){
             DetailScreen(navController = navController)
         }
         composable(route = Screen.Login.route){
             LoginScreen(navController  = navController)
         }
         composable(route = Screen.Signup.route){
             SignUpScreen(navController = navController)
         }
    }
}

注:
至此,就可以运行项目,显示主页面了。

但是细心的小伙伴,发现了没有,其实我们平时做项目,可能是多模块开发,

所以,我们现在可以把 HomeDetail 封装成一模块,把 LoginSignup 分成一个模块

第四步,通过 NavGraphBuilder 的扩展函数 navigation 分组

@Composable
fun SetupNavGraph(navController: NavHostController){
    NavHost(navController = navController, startDestination = HOME_ROUTE, route = ROOT_ROUTE){
        navigation(startDestination = Screen.Home.route,route = HOME_ROUTE){
            composable(route = Screen.Home.route){
                HomeScreen(navController   = navController)
            }
            composable(route = Screen.Detail.route){
                DetailScreen(navController = navController)
            }
        }
        navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
            composable(route = Screen.Login.route){
                LoginScreen(navController  = navController)
            }
            composable(route = Screen.Signup.route){
                SignUpScreen(navController = navController)
            }
        }
    }
}

注:
看上面代码,其实还可以优化,可以通过kotlin的扩展函数,来做分组

第五步,通过 NavGraphBuilder 的扩展函数 分组

HomeNavGraph.kt

fun NavGraphBuilder.homeNavGraph(navController: NavHostController){
    navigation(startDestination = Screen.Home.route,route = HOME_ROUTE){
        composable(route = Screen.Home.route){
            HomeScreen(navController   = navController)
        }
        composable(route = Screen.Detail.route){
            DetailScreen(navController = navController)
        }
    }
}

HomeNavGraph.kt

fun NavGraphBuilder.authNavGraph(navController: NavHostController){
    navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
        composable(route = Screen.Login.route){
            LoginScreen(navController  = navController)
        }
        composable(route = Screen.Signup.route){
            SignUpScreen(navController = navController)
        }
    }
}

NavGraph.kt

@Composable
fun SetupNavGraph(navController: NavHostController){
    NavHost(navController = navController, startDestination = HOME_ROUTE, route = ROOT_ROUTE){
        homeNavGraph(navController = navController)
        authNavGraph(navController = navController)
    }
}

注:
现在看下Graph类的代码是不是简洁很多,而且子模块的分组逻辑清晰,

上面代码有个注意的点: startDestination = HOME_ROUTE,这里指向了 navigation 扩展函数的route的值

第六步,跳转的例子

Main - > Login (各模块间跳转)

HomeScreen

@Composable
fun HomeScreen(navController: NavController){
Column(...) {
        Text(modifier = Modifier.clickable {
            navController.navigate(AUTHENTICATION_ROUTE)
        },
            ...)
        ...
   }
}

AuthNavGraph.kt

fun NavGraphBuilder.authNavGraph(navController: NavHostController){
    navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
        composable(route = Screen.Login.route){
            LoginScreen(navController  = navController)
        }
        composable(route = Screen.Signup.route){
            SignUpScreen(navController = navController)
        }
    }
}

注:navController.navigate 指向 navigationroute 的值,startDestination的值是 Screen.Login.route,因此跳转到了登录页面

Login - > Signup (单独模块内各页面间跳转)

LoginScreen.kt

@Composable
fun LoginScreen(navController: NavController){
    Column(...) {
        Text(modifier = Modifier.clickable {
            navController.navigate(route = Screen.Signup.route)
        },
           ...)
        ...
    }
}
SignUp -> Login (返回上一级)

SignUpScreen.kt

@Composable
fun SignUpScreen(navController: NavController){
    Box(...) {
        Text(modifier = Modifier.clickable {
            navController.popBackStack()
        },
        ...)
    }
}
Login - > Home (返回不同模块的第一个页面)

LoginScreen

@Composable
fun LoginScreen(navController: NavController){
    Column(...) {
        Text(...)

        Text(modifier = Modifier.padding(top = 150.dp).clickable {
            navController.navigate(HOME_ROUTE){
                popUpTo(HOME_ROUTE)
            }
        },
        ...)
    }
}

注:popUpTo 清除当前栈顶到节点 HOME_ROUTE 之间所有节点(不包含 Screen.Home.route

Login - > Home -> Detail (返回不同模块上一级,再跳转到不同模块下级页面)
@Composable
fun LoginScreen(navController: NavController){
    Column(...) {
        Text(...)

        Text(...)

        Text(modifier = Modifier.padding(top = 30.dp).clickable {
            navController.popBackStack()
            navController.navigate(Screen.Detail.route)
        },
        ...)
    }
}

注:
navController.popBackStack() 返回不同模块上一级,navController.navigate(Screen.Detail.route) 跳转到不同模块下级页面

SignUp -> Home (最顶级页面 跳转到 首页,并且设置为 首页 为 栈顶模式 )
@Composable
fun SignUpScreen(navController: NavController){
    Column(...) {
        Text(...)

        Text(modifier = Modifier.clickable {
             navController.navigate(HOME_ROUTE){
                 popUpTo(HOME_ROUTE){ inclusive = true}
             }
        },
        ...)

        Text(modifier = Modifier.clickable {
            navController.navigate(HOME_ROUTE){
                launchSingleTop
                popUpTo(HOME_ROUTE)
            }
        },
        ...)
    }
}

注:
至此,页面之间跳转的各种情况都讲到了,如有发现其他问题和补充,欢迎反馈和评论区留言

后续关于页面之间的传参,单独写篇文章讲解,后续再会…


总结

  1. Compose Navigation 是一个用于 Jetpack Compose 的导航组件,它提供了简单而强大的 API 来处理页面跳转用户界面交互
  2. NavController 是导航组件的中心 API,负责 控制页面导航的流程,通过 rememberNavController() 创建
  3. NavHost 是一个 Composable 函数,用于 承载导航的页面,同时也是 承载导航页面的容器
  4. 通过 navController.navigate(route路径) 导航页面
  5. 通过 navController.popBackStack() 返回上一页面
  6. 通过 popUpTo 清除 当前栈顶当前节点 之间的 所有节点
  • 27
    点赞
  • 32
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

谭祖爱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值