ARTICLE · 1295370
保姆级学习开发安卓手机软件实战(十八)--Navigation路由补充(带传入参数)
保姆级学习开发安卓手机软件实战(十八)--Navigation路由补充(带传入参数)之前我们切换界面用的Navigation,但是都是没有传入参数的界面,这次我们来说说怎么加上有参数的路由。 以本软件为说明用例,我们的设备列表界面存在设备后,点击单个设备卡片,可以进入该设备的详细展示界面。这时候,就需要把设备id传入打开窗体中。 首先先把设备详细界面的框架搞出来(里面内容我就不加了哈)。 新建ui\screen\DeviceDetailViewModel.kt(主要就是之前我们设备列表界面是接受所有设备信息,这里筛选出选择仪器的信息): 这个界面只放了一个标题栏显示我们进入的是哪个仪器界面,可以看出绑定vm的方式有所不同,要传入devId。 那还有因为我们是设备列表点进来的卡片,所以设备列表界面也要做修改: 
然后一层层传进去: 



这一层层有点晕哈,如果大家没有分出很多文件,不需要这么多层哈,我是因为模块复用了,反正总的意思就是点击卡片,在最里层我们是知道设备id的,然后一层层传出去。 然后卡片的点击事件也记得要完善哈。 
最后是新增路由: 
有关导航,也就是设备id通过设备列表界面传出来了,然后要跳转到设备详情页: 
然后设备详情页的导航设置:
package com.example.jstz1.ui.screenimport android.app.Applicationimport androidx.lifecycle.AndroidViewModelimport androidx.lifecycle.viewModelScopeimport com.example.jstz1.data.model.DeviceStateimport com.example.jstz1.data.remote.DeviceEventimport com.example.jstz1.data.remote.DeviceSessionManagerimport com.example.jstz1.data.remote.ManualCtrlimport com.example.jstz1.data.remote.PacketBuilderimport kotlinx.coroutines.flow.MutableStateFlowimport kotlinx.coroutines.flow.StateFlowimport kotlinx.coroutines.flow.asStateFlowimport kotlinx.coroutines.launch/*** 设备详情页ViewModel* @param application Application上下文,AndroidViewModel要求,避免持有Activity上下文导致内存泄漏* @param devId 当前页面查看的设备ID,固定为本页面的目标设备*/class DeviceDetailViewModel(application: Application,private val devId: String) : AndroidViewModel(application) {/*** 设备最新状态流* null:离线 / 尚未收到该设备的状态数据* MutableStateFlow:内部可变,仅ViewModel内部修改*/private val _state = MutableStateFlow<DeviceState?>(null)/*** 对外暴露只读状态流,Compose页面collect观察UI刷新*/val state: StateFlow<DeviceState?> = _state.asStateFlow()init {// viewModelScope:ViewModel生命周期协程,ViewModel销毁自动cancel,不会泄漏viewModelScope.launch {// 全局设备事件管理器:订阅所有设备推送过来的事件DeviceSessionManager.events.collect { e ->when (e) {// 设备状态更新事件is DeviceEvent.StateUpdated -> {// 只处理和当前页面devId匹配的设备,过滤其他设备事件if (e.state.devId == devId) {_state.value = e.state}}// 设备离线事件is DeviceEvent.Offline -> {if (e.devId == devId) {// 置为null,UI上展示离线_state.value = null}}// 设备上线事件:当前逻辑暂时不单独处理,按需在这里加逻辑is DeviceEvent.Online -> Unit}}}}}
新建ui\screen\DeviceDetailScreen.kt:/*** 设备详情页面* @param devId 设备唯一ID【路由路径参数,由navController跳转时传入】* @param ip 可选设备ip,非路由必传参数,按需传递* @param onBack 返回上一页回调* @param modifier 外层修饰符* @param currentUser 当前登录用户信息,用于顶部栏展示* @param onLogout 登出回调,点击顶部栏登出按钮触发*/@Composableinternal funDeviceDetailScreen(devId: String,ip: String? = null,onBack: () -> Unit,modifier: Modifier = Modifier,currentUser: String? = null,onLogout: () -> Unit = {}) {// 获取Application上下文,给ViewModel使用,避免内存泄漏(不要直接拿Activity context)val app: android.app.Application =LocalContext.current.applicationContext as android.app.Application// 构造ViewModel,传入Application + 路由拿到的devId// viewModelFactory:自定义工厂,用来给ViewModel传构造参数(devId不能无参构造)val vm: DeviceDetailViewModel = viewModel(factory = viewModelFactory {initializer { DeviceDetailViewModel(app, devId) }})Surface(modifier.fillMaxSize(), color = MaterialTheme.colorScheme.background) {Column(Modifier.fillMaxSize()) {// 顶部导航栏:标题展示devId,支持返回、展示用户、登出TopBar(title = devId,onBack = onBack,currentUser = currentUser,onLogout = onLogout)}}}







const val DEV_DETAIL = "devDetail/{devId}"// 函数:根据 devId 拼接出真实路由fundetail_Dev(devId: String)="devDetail/$devId"

// 带参数的详情页composable(route = Route.DEV_DETAIL,arguments = listOf(navArgument("devId") { type = NavType.StringType })//arguments:声明这个路由需要接收的参数定义) { entry ->// entry = backStackEntry:导航栈条目,存放本次跳转携带的所有参数// 从导航参数包里取出 devId;为空兜底返回空字符串val devId = entry.arguments?.getString("devId").orEmpty()DeviceDetailScreen(devId = devId,//传入界面供界面和vm使用currentUser = currentUser,onLogout = { Logout() },onBack = { navController.popBackStack() })}















































































