为Android构建现代应用——应用导航设计

在前一章节的实现中,Skeleton: Main structure,我们留下了几个 Jetpack 架构组件,这些组件将在本章中使用,例如 Composables、ViewModels、Navigation 和 Hilt。此外,我们还通过 Scaffold 集成了 TopAppBar 和 BottomAppBar UI 模式的基本结构。为了继续改进这个实现,我们需要添加一个新的关键选项卡,即“应用程序的通用状态”。

App程序的状态:一般状态

定义导航地图

导航从其他UI元素

摘要


应用程序状态:一个通用状态


 “设计原则”中我们讨论了状态在现代 Android 应用程序中的重要作用。
设计中可能存在三种类型的状态:属性 UI 状态、组件 UI 状态和屏幕 UI 状态。
除了这些状态之外,我们还可以定义一种新类型的状态,即应用程序状态。
这个新状态将定义应用程序的通用状态。它将用于屏幕之间的导航、自发消息(snackbars)的呈现以及应用程序中其他可用的进程。
在主目录中,我们将定义一个名为 OrderNowState 的类,它将是我们的状态持有者,代表这种类型的状态。

接下来,我们像这样执行OrderNowState的初始实现:

@SuppressLint("RememberReturnType")
@Composable
fun rememberAppState(scaffoldState:ScaffoldState=rememberScaffoldState(),navController: NavHostController =rememberNavController(),resources1: Resources = resources(),coroutineScope: CoroutineScope=rememberCoroutineScope()
)=remember(scaffoldState,navController,resources1,coroutineScope){OrderNowState(scaffoldState,navController,resources1,coroutineScope)
}class OrderNowState(val scaffoldState:ScaffoldState,val navController:NavHostController,private val resources:Resources,coroutineScope:CoroutineScope)@Composable
@ReadOnlyComposable
fun resources():Resources{LocalConfiguration.currentreturn LocalContext.current.resources
}
然后,我们修改我们的OrderNowScreen视图以包含之前定义的状态,如下所示:
@Preview
@Composable
fun OrderNowScreen(){MyTestTheme {Surface(modifier = Modifier.fillMaxSize(),color=MaterialTheme.colors.background) {val appState =rememberAppState()Scaffold(scaffoldState = appState.scaffoldState, topBar = { OrderNowTopBar()}, bottomBar = {OrderNowBottomBar()}){contentPadding->println(contentPadding)}}}
}

注意:

还需要向OrderNowScreen添加资源函数,它将使用该函数访问App资源。

突出显示更改的代码行如下:

  Scaffold(scaffoldState = appState.scaffoldState,topBar = { OrderNowTopBar() },bottomBar = { OrderNowBottomBar() }) { contentPadding ->println(contentPadding)}

有了上面的代码,我们现在可以告诉Scaffold它应该把哪个状态作为引用:应用程序的状态。之后,视图之间的导航操作、自发消息的呈现,以及视图只把AppState作为真实源的其他独占任务都将被允许。

既然已经为APP定义了状态,我们就可以继续实现APP的导航了。

定义导航地图

我们在应用程序中使用的导航策略由以下元素组成:

NavHost:它是负责在视图中显示导航结果的组件。导航的结果由NavigationController和导航图中给出的定义决定。

AppSoGraph:它是导航图的实现。它应该根据指定的路由将导航指向哪个视图或可组合对象。

屏幕路由:它们是可以通过导航到达的应用程序的不同屏幕。无论导航是从选项菜单、链接、按钮还是任何其他活动代理激活的,都无关紧要。每个屏幕都有一个与之关联的唯一路由。

一般导航图

 我们将继续在OrderNow中包含这些元素

OrderNowScreenRoute

首先,创建一个名为common -> navigation的新遍历目录。在这个包中,我们像这样添加了一个名为OrderNowScreenRoute的类:

 在这个类中,可以导航到的屏幕定义如下:

sealed class OrderNowScreenRoute(val route:String){object Home :OrderNowScreenRoute("home")object Cart:OrderNowScreenRoute("cart")object ProductList:OrderNowScreenRoute("product_list")object ProductDetail:OrderNowScreenRoute("product_detail")
}

OrderNowNavHost 和 AppSoGraph

现在,我们创建OrderNowNavHost类,它将像这样表示应用程序的NavHost:

@Composable
fun OrderNowNavHost(appState:OrderNowState){NavHost(navController = appState.navController,5,startDestination=1){appSoGraph(appState)}
}fun NavGraphBuilder.appSoGraph(appState: OrderNowState) {}

从之前的代码片段中,我们应该强调以下定义:
• OrderNowNavHost 需要知道 APP 的状态。
• NavController 是从 APP 状态中托管和获取的。
• Navigation map(appSoGraph)将基于 APP 的状态创建,并且是在 OrderNowNavHost 内定义的扩展。
为了继续完成 OrderNow 中导航的实现,我们必须添加一个描述如下的辅助类。
NavigationBarSection
NavigationBarSection 是一个辅助类,代表应用程序底部菜单中组成筛选组的部分。
请记住,我们可以从选项菜单或其他 UI 组件(如链接、按钮或内部重定向)中的操作开始导航。
在下一节中,我们将对内部重定向(从按钮、链接等)进行更改;现在,让我们专注于从 BottomBar 导航。

我们看到NavigationBarSection帮助器类将如何只对Home和Cart屏幕进行分组,这是我们希望从BottomBar菜单启用的选项。这个类将像这样放置在导航目录中:

它的实现是这样的:

sealed class OrderNowScreenRoute(val route:String){object Home :OrderNowScreenRoute("home")object Cart:OrderNowScreenRoute("cart")object ProductList:OrderNowScreenRoute("product_list")object ProductDetail:OrderNowScreenRoute("product_detail")
}sealed class NavigationBarSection(val title: String, val icon: ImageVector, val route:String){companion object{val sections = listOf(OrderNowScreenRoute.Home,OrderNowScreenRoute.Cart)}object Home:NavigationBarSection(title ="Home" , icon = Icons.Default.Home, route =OrderNowScreenRoute.Home.route)object Cart:NavigationBarSection(title = "Cart", icon =Icons.Default.ShoppingCart, route = OrderNowScreenRoute.Cart.route)
}

将helper类添加到项目中后,我们继续更新OrderNowNavHost类,如下所示:

@Composable
fun OrderNowNavHost(appState: OrderNowState, paddingValues: PaddingValues) {NavHost(navController = appState.navController,startDestination = NavigationBarSection.Home.route,modifier = Modifier.padding(paddingValues)) {appSoGraph(appState)}
}fun NavGraphBuilder.appSoGraph(appState: OrderNowState) {composable(NavigationBarSection.Home.route) {// HomeScreen()}composable(NavigationBarSection.Cart.route) {// CartScreen()}composable(OrderNowScreenRoute.ProductList.route){//ProductListScreen()}composable(OrderNowScreenRoute.ProductDetail.route){//ProductDetailScreen()}}

appSoGraph函数的实现是NavGraphBuilder的扩展,在那里,我们为App的每个屏幕指定导航地图。另外,通过startDestination参数,指定将首先呈现的默认屏幕,即Home屏幕。采用更改的下一步是更新名为 OrderNowBottomBar 的类,如下所示:

@Composable
fun OrderNowBottomBar(navController: NavHostController) {val navBackStackEntry by navController.currentBackStackEntryAsState()val currentDestination = navBackStackEntry?.destinationBottomNavigation(backgroundColor = MaterialTheme.colors.background,contentColor = contentColorFor(MaterialTheme.colors.background),elevation = 10.dp) {NavigationBarSection.sections.forEach { section ->val selected = currentDestination?.hierarchy?.any {it.route == section.route} == trueBottomNavigationItem(icon = {Icon(imageVector =  ImageVector.vectorResource(R.drawable.ic_launcher_foreground),contentDescription = stringResource(id = R.string.app_name))},label = { Text(text = stringResource(id =R.string.app_name)) },selected = selected,unselectedContentColor = Color.Gray,selectedContentColor = Color.Red,onClick = {navController.navigate(section.route) {popUpTo(navController.graph.findStartDestination().id) {saveState = true}launchSingleTop = truerestoreState = true}})}}
}

对于添加到 NavigationBarSection 中的每个项目,BottomBar 中都会显示一个选项。OrderNowBottomBar 的这个实现比之前的架构:主要结构中的实现更清晰。

然后,我们再次更新 OrderNowScreen 视图,如下所示:


@Preview
@Composable
fun OrderNowScreeen(){MyTestTheme(){Surface {val appState =rememberAppState()Scaffold(scaffoldState = appState.scaffoldState,topBar = {OrderNowTopBar()},bottomBar = {OrderNowBottomBar(appState.navController)}){contentPadding ->OrderNowNavHost(appState,contentPadding)}}}
}

现在 OrderNowBottomBar 将需要引用负责导航的 navController。

在Scaffold的内容部分,添加了OrderNowNavHost的实例,该实例接收APP的一般状态作为参数。Order Now with simple navigation:

从其他UI元素进行导航

 现在我们已经准备好从 BottomBar 选项导航的实现,我们需要定义从其他 UI 元素(例如按钮、链接、DeepLinks)甚至根据应用程序的其他内部组件的请求以编程方式导航应用程序。我们要做的第一个更改是添加一个名为 OrderNowNavigationState 的结构,它允许我们扩展应用程序的一般状态。

OrderNowNavigationState,是APP通用状态的扩展,即一组用于导航目的的OrderNowState状态的扩展。我们还将使用此结构来集中取决于应用程序状态的导航逻辑。

OrderNowNavigationState的实现如下:


fun OrderNowState.popUp(){navController.popBackStack()
}fun OrderNowState.navigate(route:String){navController.navigate(route){launchSingleTop  = true}
}fun OrderNowState.navigateAndPopUp(route:String,popUp:String){navController.navigate(route){launchSingleTop =truepopUpTo(popUp){inclusive =true}}
}fun OrderNowState.navigateSaved(route:String,popUp:String){navController.navigate(route){launchSingleTop =truerestoreState =truepopUpTo(popUp){saveState =true}}
}fun OrderNowState.clearAndNavigate(route:String){navController.navigate(route){launchSingleTop =truepopUpTo(0){ inclusive =true}}
}

对Home、ProductList和ProductDetail屏幕做了一些更改,如下图所示:

 以in - home视图为例,导航动作通过Button执行,方式如下:


@Composable
fun HomeScreen(goToProductList:()->Unit,modifier:Modifier=Modifier,viewModel2:HomeViewModel =hiltViewModel()){Column(){Button(onClick = goToProductList,){Text(text = stringResource(id = R.string.app_name))}}
}

代码的重要部分是“Go to —> ProductList Screen”按钮的动作 onClick = goToProductList 的定义,其中通过设计原理章节中解释的状态提升技术,我们将动作 goToProductList 委托给在 OrderNowNavHost 中定义的 appSoGraph 导航图如下:

un NavGraphBuilder.appSoGraph1(appState:OrderNowState){val goToListFromHome:()->Unit ={appState.navigateSaved(OrderNowScreenRoute.ProductList.route,OrderNowScreenRoute.Home.route)}composable(NavigationBarSection.Home.route){HomeScreen(goToProductList =goToListFromHome)}
}

回想一下,navigatesave函数是OrderNowNavigationState结构中定义的扩展的一部分。同样的实现应用于导航到其他ProductList和ProductDetail屏幕,这样在OrderNowNavHost中的性能如下所示:

fun  NavGraphBuilder.appSoGraph2(appState:OrderNowState){val homeRoute= OrderNowScreenRoute.Home.routeval listRoute= OrderNowScreenRoute.ProductList.routeval detailRoute =OrderNowScreenRoute.ProductDetail.routeval goToListFromHome:()->Unit ={appState.navigateSaved(listRoute,homeRoute)}val goToDetailFromList:()->Unit ={appState.navigateSaved(detailRoute,listRoute)}val goBack:()->Unit ={appState.popUp()}composable(NavigationBarSection.Home.route){HomeScreen(goToProductList =goToListFromHome)}composable(NavigationBarSection.ProductList.route){ProductListScreen(goToProductDetail =goToDetailFromList,goBack =goBack)}composable(NavigationBarSection.ProductDetail.route){ProductDetailScreen(goBack =goBack)}}

总结起来,前面的代码段中配置了以下导航定义(导航图):

  1. 从主页(Home screen)导航到产品列表(ProductList screen)。
  2. 从产品列表(ProductList screen)导航到产品详情(ProductDetail screen)。
  3. 从产品详情(ProductDetail screen)导航回到上一个呈现的屏幕。
  4. 从底部导航栏菜单导航到主页(Home screen)。
  5. 从底部导航栏菜单导航到购物车(Cart screen)。

到此为止,我们在 OrderNow 应用中已经实现了一个很好的基本导航。然而,还有一些东西缺失。 您有什么想法可能是缺失的吗? 我们需要在导航中包含数据或信息的传递。 由于在本章中设计了相关的实现,因此很容易实现数据传递。我们将在最后“实现功能”中详细探讨。

总结

本章中,我们完成了应用程序的主要部分的设计,这些将是以后添加功能的基础。 我们知道导航是应用程序的重要组成部分,必须从应用程序设计的开始就考虑它。 在本章中,我们采用了谷歌的建议,将状态纳入导航逻辑中。 同时,读者可以注意到我们的策略中没有直接涉及 ViewModel 或 View,这使得它在测试时更加灵活,可以检查导航。

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

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

相关文章

yolov3-spp 训练结果分析:网络结果可解释性、漏检误检分析

1. valid漏检误检分析 ①为了探查第二层反向找出来的目标特征在最后一层detector上的意义!——为什么最后依然可以框出来目标,且mAP还不错的? ②如何进一步提升和改进这个数据的效果?可以有哪些优化数据和改进的地方?让…

《ChatGPT原理最佳解释,从根上理解ChatGPT》

【热点】 2022年11月30日,OpenAI发布ChatGPT(全名:Chat Generative Pre-trained Transformer), 即聊天机器人程序 ,开启AIGC的研究热潮。 ChatGPT是人工智能技术驱动的自然语言处理工具,它能够…

竞争之王CEO商战课,聚百家企业在京举行

竞争之王CEO商战课,于2023年7月29-31日在北京临空皇冠假日酒店举办,近百家位企业家齐聚一堂,共享饕餮盛宴。 竞争之王CEO商战课是打赢商战的第一课。 竞争环境不是匀速变化,而是加速变化。 在未来的市场环境中,企业间…

Day12-1-Webpack前端工程化开发

Webpack前端工程化 1 案例-webpack打包js文件 1 在index.html中编写代码 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><me…

基于Kubernetes环境的高扩展机器学习部署利器——KServe

随着ChatGPT的发布&#xff0c;人们越来越难以回避利用机器学习的相关技术。从消息应用程序上的文本预测到智能门铃上的面部识别&#xff0c;机器学习&#xff08;ML&#xff09;几乎可以在我们今天使用的每一项技术中找到。 如何将机器学习技术交付给消费者是企业在开发过程中…

【Spring Boot】请求参数传json数组,后端采用(pojo)新增案例(103)

请求参数传json数组&#xff0c;后端采用&#xff08;pojo&#xff09;接收的前提条件&#xff1a; 1.pom.xml文件加入坐标依赖&#xff1a;jackson-databind 2.Spring Boot 的启动类加注解&#xff1a;EnableWebMvc 3.Spring Boot 的Controller接受参数采用&#xff1a;Reque…

构建vue项目配置和环境配置

目录 1、环境变量process.env配置2、vue package.json多环境配置vue-cli-service serve其他用法vue-cli-service build其他用法vue-cli-service inspect其他用法3、vue导出webpack配置4、配置打包压缩图片文件5、打包去掉多余css(由于依赖问题暂时未实现)6、打包去除console.…

【Linux】进程间通信——管道

目录 写在前面的话 什么是进程间通信 为什么要进行进程间通信 进程间通信的本质理解 进程间通信的方式 管道 System V IPC POSIX IPC 管道 什么是管道 匿名管道 什么是匿名管道 匿名管道通信的原理 pipe()的使用 匿名管道通信的特点 拓展代码 命名管道 什么是命…

IDEA离线环境搭建远程开发-Windows

公司的云桌面实在太卡&#xff0c;多个微服务项目跑起来&#xff0c;直接无法进行其它编码工作&#xff0c;所以想到使用Idea提供的远程开发功能&#xff0c;将服务运行在服务器&#xff0c;电脑只提供给开发页面展示&#xff0c;提高效率。 环境介绍&#xff1a; 开发环境&…

SystemVerilog数组参数传递及引用方法总结

一、将常数数组传递给task/function 如下面的程序&#xff0c;将一个常数数组传递给function module my_array_test();function array_test(int array[4]);foreach(array[i]) begin$display("array[%0d] %0d", i, array[i]);endendfunctioninitial beginarray_tes…

景联文科技高质量成品数据集上新啦!

景联文科技近期上新多个成品数据集&#xff0c;包含图像、视频等多种类型的数据&#xff0c;涵盖丰富的场景&#xff0c;可满足不同模型的多元化需求。 高质量成品数据集可用于训练和优化模型&#xff0c;使得模型能够更加全面和精准地理解和处理任务&#xff0c;更好地应对复…

anaconda创建虚拟环境在D盘

【看一看就行&#xff0c;又是挺水的一期&#xff08;每一季都掺和一点子水分也挺好&#xff09;】 一、创建&#xff1a; conda create --prefixD:\python37\py37 python3.7 这下就在D盘了&#xff1a; 二、激活刚刚那个环境&#xff1a; activate D:\pyhton37\py37​ &…

如何微调医疗大模型llm:llama2学习笔记

三个微调方向&#xff1a;简单医疗问答 临床问答 影像学 一般流程&#xff1a; 1 数据集准备 2 模型基座选择 3 微调 4 案例拆解 1 数据集准备&#xff1a;两种类型&#xff0c;一种文本一种影像 扩展&#xff0c;多模态 2 模型基座选择 多模态处理所有视频&#xff0c;文本…

【汇总】解决Ajax请求后端接口,返回ModelAndView页面不跳转

【汇总】解决Ajax请求后端接口&#xff0c;返回ModelAndView不跳转 问题发现问题解决方法一&#xff1a;直接跳转到指定URL&#xff08;推荐&#xff09;方法二&#xff1a;将返回的html内容&#xff0c;插入到页面某个元素中方法三&#xff1a;操作文档流方法四&#xff1a;使…

redis的安装和配置

一、nosql 二、redis的安装和配置 redis的安装&#xff1a; redis常见配置&#xff1a; 配置文件redis.conf

【FAQ】调用EasyDSS返回的直播快照接口,无法编辑只能新建的原因排查与解决

EasyDSS视频直播点播平台集视频直播、点播、转码、管理、录像、检索、时移回看等功能于一体&#xff0c;可提供音视频采集、视频推拉流、播放H.265编码视频、存储、分发等视频能力服务&#xff0c;在应用场景上&#xff0c;平台可以运用在互联网教育、在线课堂、游戏直播等领域…

【H5移动端】常用的移动端方案合集-键盘呼起、全面屏适配、图片大小显示、300ms点击延迟、首屏优化(不定期补充~)

文章目录 前言键盘呼起问题靠近底部的输入项被键盘遮挡底部按钮被顶上去 全面屏适配图片大小显示问题解决300ms延迟首屏优化 前言 这篇文章总结了我在工作中做H5遇到的一些问题&#xff0c;包括我是怎么解决的。可能不是当下的最优解&#xff0c;但是能保证解决问题。 单位适…

智慧防汛,数字科技的力量

随着夏日的脚步临近&#xff0c;台风季节即将降临。对于那些居住在沿海地区的人们来说&#xff0c;台风是一种常见的自然灾害&#xff0c;其带来的风雨可能对生命和财产造成严重威胁。然而&#xff0c;随着数字科技的飞速发展&#xff0c;可视化技术为防汛抗台工作带来了全新的…

【C++】STL——queue的介绍和使用、queue的push和pop函数介绍和使用、queue的其他成员函数

文章目录 1.queue的介绍2.queue的使用2.1queue构造函数2.2queue的成员函数&#xff08;1&#xff09;empty() 检测队列是否为空&#xff0c;是返回true&#xff0c;否则返回false&#xff08;2&#xff09;size() 返回队列中有效元素的个数 &#xff08;3&#xff09;front() 返…

在使用Python爬虫时遇到解析错误解决办法汇总

在进行Python爬虫任务时&#xff0c;遇到解析错误是常见的问题之一。解析错误可能是由于网页结构变化、编码问题、XPath选择器错误等原因导致的。为了帮助您解决这个问题&#xff0c;本文将提供一些实用的解决办法&#xff0c;并给出相关的代码示例&#xff0c;希望对您的爬虫任…