Compose | UI组件(十四) | Navigation-Data - 页面导航传递数据
文章目录
- 前言
- 传参流程
- 实例说明
- 普通方式传值
- 定义接受参数格式
- 定义接受参数类型
- 获取参数
- 传入参数
- 传参和接受参数效果图
- 结合 ViewModel 传递参数
- 定义ViewModel
- 在 navigation 定义 ViewModel 实例,并且传入 LoginScreen
- 传入输入框中的值,并且跳转传值
- 获取值
- Parcelable 传递参数
- 定义一个数据类
- 自定义定义一个通用的NavType
- 设置一个接受参数的占位符
- 传递参数
- 接收参数
- 总结
前言
在 Compose 中使用 Navigation 组件进行页面跳转时,可以使用 NavController 和 NavHost 来传递参数。
传参流程
- 使用 NavController 传递参数:
NavController 是 Navigation 组件的核心类,用于控制页面导航。你可以在 NavController 中使用
navigate() 方法传递参数。这些参数可以在目标页面中通过参数名称获取并使用。
例如:
navController.navigate("SecondScreen") {
// 传递参数
arguments = remember { mutableStateOf("key") }
arguments?.putString("key", "value")
}
在目标页面中,可以通过 arguments 参数获取传递的参数:
val value = arguments?.getString("key") ?: "default value"
- 使用 NavHost 传递参数:
在 NavHost 中,可以使用 composable() 函数定义导航路径和参数。在 composable()
函数中,可以指定传递的参数和接收参数的方法。
例如:
NavHost(navController = navController, startDestination = "MainScreen") {
composable("MainScreen") { MainScreen(arguments = listOf("value1", "value2")) }
composable("SecondScreen") { SecondScreen(arguments = listOf("value3", "value4")) }
}
在目标页面中,可以通过 arguments 参数获取传递的参数:
val values = arguments[0] + arguments[1] // value1value2 for MainScreen, value3value4 for SecondScreen
这些是在 Compose 中使用 Navigation 组件进行参数传递的一些常见方法。具体实现可能因不同的平台、框架或技术而有所差异。
实例说明
下面使用登录页面传入用户名和密码,到详情页面,做为讲解案例
普通方式传值
就是通过 (状态)State 控制变量的传值
定义接受参数格式
object Detail:Screen(route = "$DETAIL?id={id},name={name},password={password}")
注:$DETAIL?id={id},name={name},password={password}
改为 $DETAIL/id={id},name={name},password={password}
。将 ?
改为 /
,如果没有传值,程序就会崩溃,/
默认是必填的,?
默认是选填的
定义接受参数类型
composable(route = Screen.Detail.route,
arguments = listOf(
navArgument(DETAIL_ARGUMENT_ID){
type = NavType.IntType
defaultValue = 0
},
navArgument(DETAIL_ARGUMENT_NAME){
type = NavType.StringType
defaultValue = "name is null"
},
navArgument(DETAIL_ARGUMENT_PASSWORD){
type = NavType.StringType
defaultValue = "password is null"
}
)
)
const val DETAIL_ARGUMENT_ID = "id"
const val DETAIL_ARGUMENT_NAME = "name"
const val DETAIL_ARGUMENT_PASSWORD = "password"
获取参数
composable(route = Screen.Detail.route,
...){navBackStackEntry ->
val id = navBackStackEntry.arguments?.getInt("id") ?: 0
val name = navBackStackEntry.arguments?.getString("name") ?: ""
val password = navBackStackEntry.arguments?.getString("password") ?: ""
DetailScreen(id,name,password,navController = navController)
}
传入参数
object Detail:Screen(route = "..."){
fun passIdAndName(id:Int= 0,name:String="tanZuAi",password:String="tanZuAi123"):String{
return "$DETAIL?id=${id},name=${name},password=${password}"
}
}
navController.navigate(Screen.Detail.passIdAndName())
传参和接受参数效果图
至此,基本的传参,就已经可以实现了
结合 ViewModel 传递参数
就是通过结合 ViewModel 传参
定义ViewModel
LoginViewModel.kt
class LoginViewModel: ViewModel(){
var nameText by mutableStateOf("")
fun onTextChanged(newString:String){
nameText = newString
}
}
在 navigation 定义 ViewModel 实例,并且传入 LoginScreen
navigation(startDestination = Screen.Login.route,route = AUTHENTICATION_ROUTE){
composable(route = Screen.Login.route){
val loginViewModel = viewModel<LoginViewModel>()
LoginScreen(loginViewModel,navController = navController)
}
composable(route = Screen.Signup.route){
SignUpScreen(navController = navController)
}
}
传入输入框中的值,并且跳转传值
LoginScreen.kt
//定义ViewModel 参数
fun LoginScreen(loginViewModel:LoginViewModel = viewModel(), navController: NavController)
//注释掉状态定义的变量值
//var textName by remember { mutableStateOf("") }
//监听输入框的值,并且改变viewmodel变量的值
OutlinedTextField(value = loginViewModel.nameText,
onValueChange = {
loginViewModel.onTextChanged(it)
},
label = { Text(text = stringResource(id = R.string.app_user_name)) },
modifier = Modifier.fillMaxWidth()
)
//传值给name
navController.navigate(Screen.Detail.passIdAndName(name = loginViewModel.nameText))
获取值
Screen.kt
object Detail:Screen(route = "$DETAIL?id={id},name={name},password={password}"){
fun passIdAndName(id:Int= 0,name:String="tanZuAi",password:String="tanZuAi123"):String{
return "$DETAIL?id=${id},name=${name},password=${password}"
}
}
HomeNavGraph.kt
val name = navBackStackEntry.arguments?.getString("name") ?: ""
DetailScreen(id,name,password,navController = navController)
DetailScreen.kt
Text(modifier = Modifier.clickable {},
text = "Detail_Name: $name" ,
color = Color.Red,
style = MaterialTheme.typography.bodyLarge)
至此,结合 ViewModel 传值流程已经讲完了
Parcelable 传递参数
通过Parcelable传递一个对象数据
定义一个数据类
@Parcelize
data class EmailModel(val id:Int,var name:String,var password:String):Parcelable
自定义定义一个通用的NavType
inline fun <reified T : Parcelable> createParcelableNavType(isNullableAllowed: Boolean = false): NavType<T> {
return object : NavType<T>(isNullableAllowed) {
override val name: String
get() = "SupportParcelable"
override fun get(bundle: Bundle, key: String): T? { //从Bundle中检索 Parcelable类型
return bundle.getParcelable(key)
}
override fun parseValue(value: String): T { //定义传递给 String 的 Parsing 方法
return Gson().fromJson(value, T::class.java)
}
override fun put(bundle: Bundle, key: String, value: T) { //作为 Parcelable 类型添加到 Bundle
bundle.putParcelable(key, value)
}
}
}
navArgument("jsonParcelable"){
type = createParcelableNavType<EmailModel>()
}
设置一个接受参数的占位符
object Detail:Screen(route = "$DETAIL?key={jsonParcelable}")
传递参数
fun passIdAndName(id:Int= 0,name:String="tanZuAi",password:String="tanZuAi123"):String{
val jsonParcelable = Gson().toJson(EmailModel(id,name, password))
return "$DETAIL?key=${Uri.encode(jsonParcelable)}"
}
接收参数
val emailModel = navBackStackEntry.arguments?.getParcelable<EmailModel>("jsonParcelable")
emailModel?.apply {
DetailScreen(id,name,password,navController = navController)
}
注:至此,通过 Parcelable 传递对象数据就讲完了
总结
- 普通方式传值:$DETAIL?id={id},name={name},password={password}
- ViewModel传值:继承 ViewModel() 类,在类中定义状态变量,通过监听变量值变化,获取变量值
- Parcelable传值:通过 Gson().toJson() 转换对象,传递值