[Android]Jetpack Compose页面跳转和传值

一、页面跳转和返回

1.添加 Navigation 依赖

在你的 build.gradle (Module)文件中, 添加 Navigation Compose 依赖。

dependencies {implementation ("androidx.navigation:navigation-compose:2.5.3")
}

2.创建跳转页面

接下来,创建两个简单的 Composable 函数,分别表示两个页面。

  • 使用 navController.navigate("routeName") 来触发跳转
  • 使用 NavController.popBackStack() 方法返回到上一页
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.navigation.NavController// 主页面
@Composable
fun HomeScreen(navController: NavController) {Column {Button(onClick = { navController.navigate("detailScreen") }) {Text("跳转到详情页")}Text("这里是首页")}
}// 详情页面
@Composable
fun DetailScreen(navController: NavController) {Column {Button(onClick = { navController.popBackStack() }) {Text("返回")}Text("这里是详情页面")}
}

3.设置 Navigation Graph

定义一个 Navigation Graph 来管理你的应用导航。这包括定义所有的导航路由和相关的 Composable 函数。

  • 使用 NavController 来管理页面的跳转
  • NavHost 是一个 Composable 容器,它包含所有的导航路由。
  • 每个路由由一个唯一的字符串标识,并关联到一个 Composable 函数。
  • startDestination指定从哪个页面开始
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController// 应用的导航设置
@Composable
fun AppNavigation() {val navController = rememberNavController() // 创建 NavController// 设置 NavHost,管理导航内容NavHost(navController = navController, startDestination = "homeScreen") {composable("homeScreen") { // 首页路由HomeScreen(navController)}composable("detailScreen") { // 详情页路由DetailScreen(navController)}}
}

这里只是功能演示,直接就用上面这样硬编码了。

延伸示例:

实际开发中,一般将路由名称定义成枚举,避免输入错误,甚至还可以为路由关联页面标题。

import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.randomdt.www.business.home.HomeScreen
import com.randomdt.www.business.modules.protocol.ProtocolScreen
import com.randomdt.www.config.TextConfig
import com.randomdt.www.main.SharedViewModel
import com.randomdt.www.business.guide.GuideScreen// 应用的导航设置
// 跳转:navController.navigate("detailScreen/Hello, Detail!")
// 返回:navController.popBackStack()
@Composable
fun AppNavigation(viewModel: SharedViewModel, start: RouteList, onComplete: (Boolean) -> Unit = { _ -> Unit }) {val navController = rememberNavController() // 创建 NavController// 设置 NavHost,管理导航内容NavHost(navController = navController, startDestination = start.description) {composable(route = RouteList.Home.description) { // 首页路由HomeScreen(navController)}composable(route = RouteList.Guide.description) { // 引导页路由GuideScreen(navController, onGuideComplete = onComplete)}composable(route = RouteList.TermsOfUse.description) { // 用户协议ProtocolScreen(navController, titleText = RouteList.TermsOfUse.description, termsText = TextConfig.termsOfUseText())}composable(route = RouteList.PrivacyPolicy.description) { // 隐私政策ProtocolScreen(navController, titleText = RouteList.PrivacyPolicy.description, termsText = TextConfig.privacyPolicyText())}}
}// 路由列表
// 可以用来关联一个导航标题名称
enum class RouteList(val description: String) {Home("homeScreen"),Guide("guideScreen"),TermsOfUse("Terms Of Use"),PrivacyPolicy("Privacy Policy"),
}
class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)PrefsManager.init(this)setContent {RandomdtTheme {Surface(modifier = Modifier.fillMaxSize(),color = MaterialTheme.colorScheme.background) {MainContent()}}}}
}@Composable
fun MainContent() {val viewModel = SharedViewModel()val isDidGuideState = remember { mutableStateOf(PrefsManager.get<Boolean>(PrefKey.IS_DID_GUIDE)) }if (isDidGuideState.value) {AppNavigation(viewModel, start = RouteList.Home)} else {AppNavigation(viewModel, start = RouteList.Guide, onComplete = { isDidGuideCompleted ->isDidGuideState.value = isDidGuideCompleted})}
}

 

4.在主题中使用 AppNavigation

在你的主要 Composable 函数或者 Activity 中,调用 AppNavigation 函数来启动整个应用的导航系统。

import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.Composableclass MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {MyApp()}}
}@Composable
fun MyApp() {MaterialTheme {Surface {AppNavigation()}}
}

二、页面间传值

1.传递数据到新页面

假设我们需要从 HomeScreen 向 DetailScreen 传递一个字符串参数。首先,我们需要在导航图中定义参数。

(1).修改导航图以接受参数

import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import androidx.navigation.NavType
import androidx.navigation.compose.navArgument// 应用的导航设置
@Composable
fun AppNavigation() {val navController = rememberNavController() // 创建 NavController// 设置 NavHost,管理导航内容NavHost(navController = navController, startDestination = "homeScreen") {composable("homeScreen") { // 首页路由HomeScreen(navController)}composable(route = "detailScreen/{message}",arguments = listOf(navArgument("message") {type = NavType.StringType // 参数的类型})) { backStackEntry -> // 详情页路由DetailScreen(navController = navController,message = backStackEntry.arguments?.getString("message") ?: "No message")}}
}

(2).修改 HomeScreen 以传递参数

@Composable
fun HomeScreen(navController: NavController) {Column {Button(onClick = { navController.navigate("detailScreen/Hello, Detail!") }) {Text("跳转到详情页")}Text("这里是首页")}
}

(3).在 DetailScreen 接收和显示传入的数据

@Composable
fun DetailScreen(navController: NavController, message: String) {Column {Button(onClick = { navController.popBackStack() }) {Text("返回")}Text("这里是详情页面: $message")}
}

2.使用 ViewModel 回传数据

(1).如何创建ViewModel?

在使用 ViewModel 和 Jetpack Compose 结合的场景中,如果 ViewModel 中的数据是以响应式的方式管理的(例如使用 MutableStateLiveData 或 Flow),那么当数据更新时,与这些数据相关的 Compose UI 组件将会自动重新渲染以反映新的数据状态。

使用 MutableState:

MutableState 是 Jetpack Compose 中用于状态管理的一种方式,当状态发生变化时,所有使用该状态的 Composable 函数会自动重新绘制。

class ExampleViewModel : ViewModel() {val message = mutableStateOf("Initial Message")fun updateMessage(newMessage: String) {message.value = newMessage}
}@Composable
fun ExampleScreen(viewModel: ExampleViewModel) {Text(text = viewModel.message.value)
}
使用 LiveData:

LiveData 同样可以被用于状态管理,在 Jetpack Compose 中,你可以使用 observeAsState() 扩展函数将 LiveData 转换为 Compose 可用的状态。

class ExampleViewModel : ViewModel() {private val _message = MutableLiveData("Initial Message")val message: LiveData<String> = _messagefun updateMessage(newMessage: String) {_message.value = newMessage}
}@Composable
fun ExampleScreen(viewModel: ExampleViewModel) {val message by viewModel.message.observeAsState()message?.let {Text(text = it)}
}
使用 Flow:

Flow 是另一种在 Kotlin 中管理异步数据流的方式。在 Compose 中,你可以使用 collectAsState() 将 Flow 转换为 Compose 可用的状态。

class ExampleViewModel : ViewModel() {private val _message = MutableStateFlow("Initial Message")val message: StateFlow<String> = _message.asStateFlow()fun updateMessage(newMessage: String) {_message.value = newMessage}
}@Composable
fun ExampleScreen(viewModel: ExampleViewModel) {val message = viewModel.message.collectAsState()Text(text = message.value)
}

 (2).定义一个 ViewModel

class MainViewModel : ViewModel() {val returnedData = mutableStateOf("")fun setReturnData(data: String) {returnedData.value = data}
}

在实际的应用开发中,使用单一的 MainViewModel 来管理所有页面间的数据传递并不是最佳实践。这种做法可能会导致 ViewModel 过于臃肿和混乱,特别是在大型应用中,这可能会导致维护困难和扩展问题。

 理想的做法是使用多个 ViewModel,每个 ViewModel 管理特定页面或功能模块的状态和逻辑。

  • 页面级 ViewModel:每个页面或每组相关页面可以有自己的 ViewModel。这样,页面间的数据传递可以通过共享的更高级别 ViewModel 来实现,或者通过事件和回调来进行。

  • 共享 ViewModel:对于需要跨多个页面共享数据的情况,可以使用一个共享的 ViewModel。这个 ViewModel 可以被多个页面访问,用于存储和管理共享数据。这通常通过依赖注入框架如 Hilt 或 Dagger 实现。

示例改进:

class HomeViewModel : ViewModel() {// Home-specific data and logic
}class DetailViewModel : ViewModel() {// Detail-specific data and logicval returnedData = mutableStateOf("")fun setReturnData(data: String) {returnedData.value = data}
}class SharedViewModel : ViewModel() {// Data and logic shared between multiple screens
}

(3).让 AppNavigation 能接收 ViewModel

@Composable
fun AppNavigation(viewModel: MainViewModel) {val navController = rememberNavController() // 创建 NavController// 设置 NavHost,管理导航内容NavHost(navController = navController, startDestination = "homeScreen") {composable("homeScreen") { // 首页路由HomeScreen(navController = navController,viewModel = viewModel)}composable(route = "detailScreen/{message}",arguments = listOf(navArgument("message") {type = NavType.StringType // 参数的类型})) { backStackEntry -> // 详情页路由DetailScreen(navController = navController,message = backStackEntry.arguments?.getString("message") ?: "No message",viewModel = viewModel)}}
}

(4).将 ViewModel 传递给 AppNavigation

@Composable
fun MyApp(viewModel: MainViewModel) {MaterialTheme {Surface {AppNavigation(viewModel)}}
}class MainActivity : ComponentActivity() {override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContent {val viewModel = MainViewModel()MyApp(viewModel)}}
}

(5).返回页面中使用 ViewModel 来保存数据

@Composable
fun HomeScreen(navController: NavController, viewModel: MainViewModel) {Column {Button(onClick = { navController.navigate("detailScreen/Hello, Detail!") }) {Text("跳转到详情页")}Text("这里是首页。返回数据=${viewModel.returnedData.value}")}
}// 详情页面
@Composable
fun DetailScreen(navController: NavController, message: String, viewModel: MainViewModel) {Column {Button(onClick = {// 保存数据viewModel.setReturnData("Returned from DetailScreen")navController.popBackStack()}) {Text("设置数据并返回")}Text("这里是详情页面: $message")}
}

3.使用事件回调和状态提升回传数据

在 Jetpack Compose 中使用状态提升(State Hoisting)的模式时,通常是将状态管理的责任留给上层组件(通常是状态的拥有者),而不是让各个子组件直接修改共享状态。这种模式有几个关键的优点:

  • 单一真相来源(Single Source of Truth): 状态被保留在一个组件内部,所有对状态的修改都通过这个组件进行,这有助于避免不同组件间状态不一致的问题。

  • 可预测性与可维护性: 当状态的更新逻辑集中在一个地方时,更容易追踪状态的变化,调试和维护也更为方便。

  • 解耦: 子组件不需要知道状态是如何被管理的,它们只关心如何显示数据和如何将用户的输入通过回调传递出去。这使得组件更加灵活和独立。

(1).创建共享状态

首先,我们在包含导航逻辑的AppNavigation中创建一个共享状态。这个状态将被HomeScreenDetailScreen共同访问和修改。

@Composable
fun AppNavigation() {val navController = rememberNavController()var sharedData by remember { mutableStateOf("Initial Data") } // 共享状态NavHost(navController = navController, startDestination = "homeScreen") {composable("homeScreen") {HomeScreen(navController = navController,data = sharedData,onNavigateToDetail = { navController.navigate("detailScreen") })}composable("detailScreen") {DetailScreen(initialData = sharedData,onReturnData = { newData ->sharedData = newDatanavController.popBackStack()})}}
}

(2).创建 HomeScreen

HomeScreen中,我们展示当前的数据,并提供一个按钮来跳转到DetailScreen

@Composable
fun HomeScreen(navController: NavController, data: String, onNavigateToDetail: () -> Unit) {Column(modifier = Modifier.fillMaxSize().padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally) {Text(text = "Current Data: $data", style = MaterialTheme.typography.headlineMedium)Spacer(Modifier.height(20.dp))Button(onClick = onNavigateToDetail) {Text("Go to DetailScreen")}}
}

(3).创建 DetailScreen

DetailScreen允许用户修改数据,并通过回调函数将新数据回传给AppNavigation,从而更新共享状态,并返回HomeScreen

@Composable
fun DetailScreen(initialData: String, onReturnData: (String) -> Unit) {var text by remember { mutableStateOf(initialData) }Column(modifier = Modifier.fillMaxSize().padding(16.dp), horizontalAlignment = Alignment.CenterHorizontally) {TextField(value = text,onValueChange = { text = it },label = { Text("Enter new data") })Spacer(Modifier.height(8.dp))Button(onClick = { onReturnData(text) }) {Text("Update and Return")}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/2471.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

MQTT协议应用场景

MQTT协议应用场景 MQTT&#xff08;Message Queuing Telemetry Transport&#xff09;协议是一种基于发布/订阅模式的轻量级消息传输协议&#xff0c;它设计用于低带宽、高延迟或不可靠的网络环境。由于其高效、简单和可靠性&#xff0c;MQTT在多种应用场景中得到了广泛的应用。…

leetcode hot100_part25

2024/4/23 56.合并区间 略 189.轮转数组 使用额外数组 遍历老数组&#xff0c;每个位置的元素放到新数组的位置&#xff08;取余&#xff09;。 环状替换 这个思路也想到了但是没想出来。 也就是连续跳&#xff0c;从i位置跳到它应该在(取余后)的位置x&#xff0c;再从x位…

UE4网络图片加载库(带内存缓存和磁盘缓存)

UE4网络图片加载库,带内存缓存和磁盘缓存,支持自定义缓存大小,支持蓝图和C++代码调用 1、调用示例 2、对外暴露函数 3、源代码-网络模块 KeImageNet.h // Fill out your copyright notice in the Description page of Project Settings.#pragma once#include "CoreM…

Vue基于高德地图API封装一个地图组件

一、参考资料 高德开放平台 | 高德地图API (amap.com) 二、安装及配置 pnpm i vuemap/vue-amap --save man.ts 密钥及安全密钥需要自己到高德地图开放平台控制台获取. import { createApp } from vue import App from ./App.vue import router from ./router i…

java实现解析html获取图片或视频url

一、前言 有时在实际项目中&#xff0c;比如发布某篇文章&#xff0c;需要取文章中的某张图片作为封面&#xff0c;那么此时需要文章内容&#xff0c;获取html内容中的图片地址作为封面&#xff0c;下面讲下如何获取html中的图片或视频地址。 二、实现 1.先定义一个工具类&…

Python学习教程(Python学习路线+Python学习视频):Python数据结构

数据结构引言&#xff1a; 数据结构是组织数据的方式&#xff0c;以便能够更好的存储和获取数据。数据结构定义数据之间的关系和对这些数据的操作方式。数据结构屏蔽了数据存储和操作的细节&#xff0c;让程序员能更好的处理业务逻辑&#xff0c;同时拥有快速的数据存储和获取方…

智能合约语言(eDSL)—— 如何使用wasmtime运行合约

在我们使用高级语言生成了智能合约——WASM之后&#xff0c;接下来就是对智能合约——WASM的使用。首先&#xff0c;我们需要引入wasmtime库&#xff0c;使用wasmtime运行我们的合约。我们的Rust程序为&#xff1a; use anyhow::Result; use std::fs; use wasmtime::*;fn main…

android openGL ES详解

1、渲染线程与主线程的通信 两个线程之间的通信可以用如下方法: 在主线程中的 GLSurfaceView 实例可以调用 queueEvent( &#xff09;方法传递一个 Runnable 给后台渲染线程&#xff0c;渲染线程可以调用 Activity 的 runOnUIThread()来传递事件 (event) 给主线程。 2、顶点…

Redhawk:ATE如何产生top level sta file

我正在「拾陆楼」和朋友们讨论有趣的话题,你⼀起来吧? 拾陆楼知识星球入口 相关文章链接 redhawk: create STA file 在“redhawk: create STA file”一文中介绍了ate的用法,可以应对block level的设计,但当需要做top level分析时&

构建安全高效的前端权限控制系统

✨✨谢谢大家捧场&#xff0c;祝屏幕前的小伙伴们每天都有好运相伴左右&#xff0c;一定要天天开心哦&#xff01;✨✨ &#x1f388;&#x1f388;作者主页&#xff1a; 喔的嘛呀&#x1f388;&#x1f388; ✨✨ 帅哥美女们&#xff0c;我们共同加油&#xff01;一起进步&am…

Qt实现XYModem协议(八)

1 概述 XMODEM协议是一种使用拨号调制解调器的个人计算机通信中广泛使用的异步文件运输协议。这种协议以128字节块的形式传输数据&#xff0c;并且每个块都使用一个校验和过程来进行错误检测。使用循环冗余校验的与XMODEM相应的一种协议称为XMODEM-CRC。还有一种是XMODEM-1K&am…

单片机学习过程

继电器光耦隔离电压转换步进电机直流电机 arduino是最好用的一种&#xff0c;他提供了完整的设备库文件&#xff0c;任何外部设备只要查找相应的库&#xff0c;就可以很方便的使用 &#xff0c; 但是如果不去学习51 或stm32 或 嵌入式玩玩还可以&#xff0c;如果碰到没有实现的…

【深耕 Python】Data Science with Python 数据科学(10)pandas 数据处理(一)

写在前面 关于数据科学环境的建立&#xff0c;可以参考我的博客&#xff1a; 【深耕 Python】Data Science with Python 数据科学&#xff08;1&#xff09;环境搭建 往期数据科学博文&#xff1a; 【深耕 Python】Data Science with Python 数据科学&#xff08;2&#xf…

使用 IPAM 解决方案简化分布式网络管理

随着组织在数字领域的全球扩张&#xff0c;分布式网络是不可避免的&#xff0c;这意味着&#xff0c;随着 IT 基础设施的发展&#xff0c;组织需要适应&#xff0c;这包括在不断增长的系统需求、应用程序堆栈、各种协议和安全防御中监控、现代化和简化流程和资源。在有效管理现…

常用的正则表达式组成

正则表达式是一种强大的文本处理工具&#xff0c;用于匹配、搜索、替换、分割等多种操作。以下是正则表达式的基本组成和语法&#xff0c;以及如何使用它们来构建复杂的模式。 正则表达式语法 1. 基本字符匹配 - 普通字符: 大多数字符&#xff0c;如a-z、A-Z、0-9&#xff0c…

盲人过马路安全:科技力量赋予“隐形守护者”

作为一名资深记者&#xff0c;我始终关注着社会各群体的生活现状&#xff0c;尤其是那些面临特殊挑战的人群。今天&#xff0c;我想聚焦一个看似平常却对盲人构成重大困扰的日常场景——过马路&#xff0c;以及一款名为蝙蝠避障的辅助应用如何成为他们的盲人过马路安全的守护者…

自制Apache-Doris 2.0.4镜像Docker部署一Fe和一Be集群及遇到的问题解决

自制Apache-Doris 2.0.4镜像Docker部署一Fe和一Be集群及遇到的问题解决 文章目录 1.前言2.doris是什么&#xff1f;2.1简介2.2介绍2.3使用场景2.4架构 3.官网4.构建部署4.1 构建环境4.2 doris2.0.4的fe和be镜像构建4.2.1 fe2.0.4镜像构建脚本4.2.2 be2.0.4镜像构建4.2.3 启动脚…

OSINT技术情报精选·2024年4月第3周

OSINT技术情报精选2024年4月第3周 2024.4.22版权声明&#xff1a;本文为博主chszs的原创文章&#xff0c;未经博主允许不得转载。 1、斯坦福大学&#xff1a;《2024年人工智能指数报告》 最近&#xff0c;由李飞飞联合领导的斯坦福大学以人为本人工智能研究所&#xff08;St…

FebHost:科技企业如何规划并注册.AI域名?

为确保企业使用.AI域名的方式准确反映其对人工智能技术的关注&#xff0c;企业应考虑以下步骤&#xff1a; 了解法律和合规要求&#xff1a; 第一步是了解与 .AI 域名相关的独特法律和合规要求。由于.AI域名源于安圭拉&#xff0c;企业必须遵守安圭拉的限制和法律规定。这包括…

K8S Centos7 安装 K8S 1.26单机版

文章目录 1.机器规划1.设置hostname2.编辑/etc/hosts3.安装ntpdate并同步时间4.关闭防火墙5.关闭selinux 2.安装 Docker1.安装Docker2.安装容器运行时cri-dockerd 3.安装K8S1.禁用swap2.安装Kubernetes相关软件3.所有master节点拉取k8s镜像4.初始化Kubernetes Master节点5.安装…