《Jetpack Compose从入门到实战》第三章 定制 UI 视图

目录
- 配置颜色、字体与形状
- Welcome Page
- Login Page
- Home Page
- 主题
- CompositionLocal
配置颜色、字体与形状
-ui.theme.Color.kt
val pink100 = Color(0xFFFFF1F1)
val pink900 = Color(0xFF3F2C2C)
val white = Color(0xFFFFFFFF)
val white850 = Color(0xD9FFFFFF)
val gray = Color(0xFF232323)
- ui.theme.Type.kt
先将Nunito Sans字体家族放入 res/font,再根据设计稿写代码
val nunitoSansFamily = FontFamily(Font(R.font.nunitosans_light, FontWeight.Light),Font(R.font.nunitosans_semibold, FontWeight.SemiBold),Font(R.font.nunitosans_bold, FontWeight.Bold)
)val h1 = TextStyle(fontSize = 18.sp,fontFamily = nunitoSansFamily,fontWeight = FontWeight.Bold
)
val h2 = TextStyle(fontSize = 14.sp,letterSpacing = 0.15.sp,fontFamily = nunitoSansFamily,fontWeight = FontWeight.Bold
)
val subtitle1 = TextStyle(fontSize = 16.sp,fontFamily = nunitoSansFamily,fontWeight = FontWeight.Light
)
val body1 = TextStyle(fontSize = 14.sp,fontFamily = nunitoSansFamily,fontWeight = FontWeight.Light
)
val body2 = TextStyle(fontSize = 12.sp,fontFamily = nunitoSansFamily,fontWeight = FontWeight.Light
)
val button = TextStyle(fontSize = 14.sp,letterSpacing = 1.sp,fontFamily = nunitoSansFamily,fontWeight = FontWeight.SemiBold,color = white
)
val caption = TextStyle(fontSize = 12.sp,fontFamily = nunitoSansFamily,fontWeight = FontWeight.SemiBold
)
- ui.theme/Shape.kt
Welcome Page
@Preview(showBackground = true)
@Composable
fun WelcomeTitle() {Column(horizontalAlignment = Alignment.CenterHorizontally,modifier = Modifier.fillMaxWidth()) {Image(painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.ic_light_logo)),contentDescription = "weclome_logo",modifier = Modifier.wrapContentWidth().height(32.dp))Box(modifier = Modifier.fillMaxWidth().height(32.dp),contentAlignment = Alignment.BottomCenter) {Text(text = "Beautiful home garden solutions",textAlign = TextAlign.Center,style = subtitle1,color = gray)}}
}@Preview(showBackground = true)
@Composable
fun WelcomeButtons() {Column(horizontalAlignment = Alignment.CenterHorizontally,modifier = Modifier.fillMaxWidth()){Button(onClick = { },modifier = Modifier.height(48.dp).padding(horizontal = 16.dp).fillMaxWidth().clip(medium),colors = ButtonDefaults.buttonColors(backgroundColor = pink900)) {Text(text = "Create account",style = button,color = white)}Spacer(modifier = Modifier.height(24.dp))TextButton(onClick = { },) {Text(text = "Log in",style = button,color = pink900,)}}
}@Preview(showBackground = true)
@Composable
fun LeafImage() {Image(painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.ic_light_welcome_illos)),contentDescription = "weclome_illos",modifier = Modifier.wrapContentSize().padding(start = 88.dp))
}@Preview(showBackground = true)
@Composable
fun WelcomeContent() {Column(modifier = Modifier.fillMaxSize()) {Spacer(Modifier.height(72.dp))LeafImage()Spacer(modifier = Modifier.height(48.dp))WelcomeTitle()Spacer(modifier = Modifier.height(40.dp))WelcomeButtons()}
}@Composable
fun WelcomePage() {Box(modifier = Modifier.fillMaxSize().background(pink100)) {Image(painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.ic_light_welcome_bg)),contentDescription = "weclome_bg",modifier = Modifier.fillMaxSize())WelcomeContent()}
}
Login Page
@Preview(showBackground = true)
@Composable
fun TopText() {Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {var keywordPre = "By Clicking below you agree to our".split(" ")var keywordPost = "and consent".split(" ")for (word in keywordPre) {Text(text = word,style = body2,color = gray,)}Text(text = "Terms of Use",style = body2,color = gray,textDecoration = TextDecoration.Underline)for (word in keywordPost) {Text(text = word,style = body2,color = gray,)}}
}@Preview(showBackground = true)
@Composable
fun BottomText() {Row(modifier = Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.Center) {Text(text = " to Our ",style = body2,color = gray)Text(text = "Privacy Policy.",style = body2,color = gray,textDecoration = TextDecoration.Underline)}
}@Preview(showBackground = true)
@Composable
fun HintWithUnderline() {Column(modifier = Modifier.paddingFromBaseline(top = 24.dp, bottom = 16.dp)){TopText()BottomText()}
}@Preview(showBackground = true)
@Composable
fun LoginInputBox() {Column {OutlinedTextField(value = "",onValueChange = {},modifier = Modifier.fillMaxWidth().height(56.dp).clip(small),placeholder = {Text(text = "Email address",style = body1,color = gray)})Spacer(modifier = Modifier.height(8.dp))OutlinedTextField(value = "",onValueChange = {},modifier = Modifier.fillMaxWidth().height(56.dp).clip(small),placeholder = {Text(text = "Password(8+ Characters)",style = body1,color = gray)})}
}@Preview(showBackground = true, showSystemUi = false)
@Composable
fun LoginTitle() {Text(text = "Log in with email",modifier = Modifier.fillMaxWidth().paddingFromBaseline(top = 184.dp, bottom = 16.dp),style = h1,color = gray,textAlign = TextAlign.Center)
}@Preview(showBackground = true)
@Composable
fun LoginButton() {Button(onClick = { },modifier = Modifier.height(48.dp).fillMaxWidth().clip(medium),colors = ButtonDefaults.buttonColors(backgroundColor = pink900)) {Text(text = "Log in",style = button,color = white)}
}@Composable
fun LoginPage() {Column(Modifier.fillMaxSize().background(white).padding(horizontal = 16.dp)) {LoginTitle()LoginInputBox()HintWithUnderline()LoginButton()}
}@Composable
@Preview
fun LoginPageLightPreview() {BloomTheme() {LoginPage()}
}
Home Page
val small = RoundedCornerShape(4.dp)
val medium = RoundedCornerShape(24.dp)val shapes = Shapes(small = RoundedCornerShape(4.dp),medium = RoundedCornerShape(4.dp),large = RoundedCornerShape(0.dp)
)
data class ImageItem(val name: String, val resId: Int)val bloomBannerList = listOf(ImageItem("Desert chic", R.drawable.desert_chic),ImageItem("Tiny terrariums", R.drawable.tiny_terrariums),ImageItem("Jungle Vibes", R.drawable.jungle_vibes)
)val bloomInfoList = listOf(ImageItem("Monstera", R.drawable.monstera),ImageItem("Aglaonema", R.drawable.aglaonema),ImageItem("Peace lily", R.drawable.peace_lily),ImageItem("Fiddle leaf tree", R.drawable.fiddle_leaf),ImageItem("Desert chic", R.drawable.desert_chic),ImageItem("Tiny terrariums", R.drawable.tiny_terrariums),ImageItem("Jungle Vibes", R.drawable.jungle_vibes)
)val navList = listOf(ImageItem("Home", R.drawable.ic_home),ImageItem("Favorites", R.drawable.ic_favorite_border),ImageItem("Profile", R.drawable.ic_account_circle),ImageItem("Cart", R.drawable.ic_shopping_cart)
)@Preview(showBackground = true)
@Composable
fun BloomRowBanner() {Column {Box(Modifier.fillMaxWidth()) {Text(text = "Browse themes",style = h1,color = gray,modifier = Modifier.fillMaxWidth().paddingFromBaseline(top = 32.dp))}Spacer(modifier = Modifier.height(16.dp))LazyRow(modifier = Modifier.height(136.dp)) {items(bloomBannerList.size) {if (it != 0) {Spacer(modifier = Modifier.width(8.dp))}PlantCard(bloomBannerList[it])}}}
}@Preview(showBackground = true)
@Composable
fun BloomInfoList() {Column {Row(Modifier.fillMaxWidth(),horizontalArrangement = Arrangement.SpaceBetween) {Text(text = "Design your home garden",style = h1,color = gray,modifier = Modifier.paddingFromBaseline(top = 40.dp))Icon(painterResource(id = R.drawable.ic_filter_list),"filter",modifier = Modifier.padding(top = 24.dp).size(24.dp))}Spacer(modifier = Modifier.height(16.dp))LazyColumn(modifier = Modifier.fillMaxWidth(),contentPadding = PaddingValues(bottom = 56.dp)) {items(bloomInfoList.size) {if (it != 0) {Spacer(modifier = Modifier.height(8.dp))}DesignCard(bloomInfoList[it])}}}
}@Preview(showBackground = true)
@Composable
fun SearchBar() {Box {TextField(value = "",onValueChange = {},modifier = Modifier.fillMaxWidth().height(56.dp).clip(RoundedCornerShape(4.dp)).border(BorderStroke(1.dp, Color.Black)),leadingIcon = {Icon(painter = rememberVectorPainter(image = ImageVector.vectorResource(id = R.drawable.ic_search)),contentDescription = "search",modifier = Modifier.size(18.dp))},placeholder = {Text(text = "Search",style = body1,color = gray)},colors = TextFieldDefaults.outlinedTextFieldColors(backgroundColor = white,unfocusedBorderColor = white,focusedBorderColor = white,),)}
}@Composable
fun HomePage() {Scaffold(bottomBar = {BottomBar()}) {Column(Modifier.fillMaxSize().background(white).padding(horizontal = 16.dp)) {Spacer(modifier = Modifier.height(40.dp))SearchBar()BloomRowBanner()BloomInfoList()}}
}@Composable
fun DesignCard(plant: ImageItem) {Row(modifier = Modifier.fillMaxWidth()) {Image(painterResource(id = plant.resId),contentScale = ContentScale.Crop,contentDescription = "image",modifier = Modifier.size(64.dp).clip(RoundedCornerShape(4.dp)))Spacer(modifier = Modifier.width(16.dp))Column {Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {Column {Text(text = plant.name,style = h2,color = gray,modifier = Modifier.paddingFromBaseline(top = 24.dp))Text(text = "This is a description",style = body1,color = gray,modifier = Modifier)}Checkbox(modifier = Modifier.padding(top = 24.dp).size(24.dp),checked = false,onCheckedChange = {// plant.enable = it},colors = CheckboxDefaults.colors(checkmarkColor = white))}Divider(color = gray, modifier = Modifier.padding(top = 16.dp), thickness = 0.5.dp)}}
}@Composable
fun PlantCard(plant: ImageItem) {Card(modifier = Modifier.size(136.dp).clip(RoundedCornerShape(4.dp))) {Column {Image(painterResource(id = plant.resId),contentScale = ContentScale.Crop,contentDescription = "image",modifier = Modifier.fillMaxWidth().height(96.dp))Box(Modifier.fillMaxWidth().padding(start = 16.dp)) {Text(text = plant.name,style = h2,color = gray,modifier = Modifier.fillMaxWidth().paddingFromBaseline(top = 24.dp, bottom = 16.dp))}}}
}@Composable
fun BottomBar() {BottomNavigation(elevation = 16.dp,modifier = Modifier.fillMaxWidth().height(56.dp).background(pink100)) {navList.forEach {BottomNavigationItem(onClick = {},icon = {Icon(painterResource(id = it.resId),contentDescription = null,modifier = Modifier.size(24.dp),)},label = {Text(it.name,style = caption,color = gray,)},selected = ("Home" == it.name))}}
}@Preview(showBackground = true)
@Composable
fun BottomBarPreview() {BloomTheme() {BottomBar()}
}@Preview(showBackground = true)
@Composable
fun DesignCardPreview() {BloomTheme() {DesignCard(bloomInfoList[0])}
}@Preview(showBackground = true)
@Composable
fun PlantCardPreview() {BloomTheme() {PlantCard(bloomBannerList[0])}
}@Preview(showBackground = true)
@Composable
fun HomePageLightPreview() {BloomTheme() {HomePage()}
}
主题
private val BloomLightColorPaltte = lightColors(primary = pink100,secondary = pink900,background = white,surface = white850,onPrimary = gray,onSecondary = white,onBackground = gray,onSurface = gray,
)private val BloomDarkColorPaltte = darkColors(primary = green900,secondary = green300,background = gray,surface = white150,onPrimary = white,onSecondary = gray,onBackground = white,onSurface = white850
)open class WelcomeAssets private constructor(var background: Int,var illos: Int,var logo: Int
) {object LightWelcomeAssets : WelcomeAssets(background = R.drawable.ic_light_welcome_bg,illos = R.drawable.ic_light_welcome_illos,logo = R.drawable.ic_light_logo)object DarkWelcomeAssets : WelcomeAssets(background = R.drawable.ic_dark_welcome_bg,illos = R.drawable.ic_dark_welcome_illos,logo = R.drawable.ic_dark_logo)
}internal var LocalWelcomeAssets = staticCompositionLocalOf { WelcomeAssets.LightWelcomeAssets as WelcomeAssets }val MaterialTheme.welcomeAssets@Composable@ReadOnlyComposableget() = LocalWelcomeAssets.currentenum class BloomTheme {LIGHT, DARK
}@Composable
fun BloomTheme(theme: BloomTheme = BloomTheme.LIGHT, content: @Composable() () -> Unit) {CompositionLocalProvider(LocalWelcomeAssets provides if (theme == BloomTheme.DARK) WelcomeAssets.DarkWelcomeAssets else WelcomeAssets.LightWelcomeAssets,) {MaterialTheme(colors = if (theme == BloomTheme.DARK) BloomDarkColorPaltte else BloomLightColorPaltte,typography = bloomTypoGraphy,shapes = shapes,content = content)}
}
CompositionLocal
CompositionLocal 是 Jetpack Compose 中的一种数据传递方式。它可以在组合组件之间传递可变数据,而无需通过 props 或 state 管理器来传递数据。这个特性比传统的数据传递方式更为高效和方便。
例如,我们可以通过 CompositionLocal 在应用程序的不同部分中传递数据,如主题、语言环境、字体等。这个特性可以让我们在组合应用程序中更轻松地使用全局状态,而不必每次都传递数据。
此外,CompositionLocal 还可以用于实现本地化和自定义主题等功能的高效性。因此,它是 Jetpack Compose 中非常重要的一个组成部分。
- 如果CompositonLocal提供得知发生更改的可能性很小或永远无法改变,利用staticCompositionLocal能显著提升性能
总结
- 所有视图组件建议用顶级函数声明
随笔:
- 简历 遇到什么问题,用了什么方法,如何进行优化
《Jetpack Compose从入门到实战》第一章 全新的 Android UI 框架
《Jetpack Compose从入门到实战》 第二章 了解常用UI组件
相关文章:
《Jetpack Compose从入门到实战》第三章 定制 UI 视图
目录 配置颜色、字体与形状Welcome PageLogin PageHome Page 主题CompositionLocal 配置颜色、字体与形状 -ui.theme.Color.kt val pink100 Color(0xFFFFF1F1) val pink900 Color(0xFF3F2C2C) val white Color(0xFFFFFFFF) val white850 Color(0xD9FFFFFF) val gray Col…...
Kubernetes组件和架构简介
目录 一.概念简介 1.含义: 2.主要功能: 3.相关概念: 二.组件和架构介绍 1.master:集群的控制平面,管理集群 2.node:集群的数据平面,为容器提供工作环境 3.kubernetes简单架构图解 一.概…...
ElementUI实现增删改功能以及表单验证
目录 前言 BookList.vue action.js 展示效果 前言 本篇还是在之前的基础上,继续完善功能。上一篇完成了数据表格的查询,这一篇完善增删改,以及表单验证。 BookList.vue <template><div class"books" style"pa…...
C++中有哪些运算符以及它们的优先级?
C中常用的运算符包括算术运算符、赋值运算符、关系运算符、逻辑运算符、位运算符等。这里列举一些常见的运算符以及它们的优先级(从高到低): 圆括号()一元正号、一元负号-数组下标[]成员选择符(点号.、箭头…...
uboot启动流程-涉及_main汇编函数
一. uboot启动流程涉及函数 本文简单分析一下 save_boot_params_ret调用的函数:_main汇编函数。 本文继之前文章的学习,地址如下: uboot启动流程-涉及s_init汇编函数_凌肖战的博客-CSDN博客 二. uboot启动流程涉及的 _main汇编函数 经过之…...
此芯科技加入百度飞桨硬件生态共创计划,加速端侧AI生态布局
近日,此芯科技(上海)有限公司(以下简称“此芯科技”)与百度签署硬件生态共创计划合作协议,正式加入由百度发起的硬件生态共创计划。双方将共同推动端侧AI和大模型在个人计算、车载计算以及元宇宙计算等领域…...
Unity把UGUI再World模式下显示到相机最前方
Unity把UGUI再World模式下显示到相机最前方 通过脚本修改Shader 再VR里有时候要把3D的UI显示到相机最前方,加个UI相机会坏事,可以通过修改unity_GUIZTestMode来解决。 测试用例 测试用例如下: 场景包含一个红色的盒子,一个UI…...
nodejs+vue活鲜物流监控系统elementui
第3章 系统分析 5 3.1 需求分析 5 3.2 系统可行性分析 5 3.2.1技术可行性:技术背景 5 3.2.2经济可行性 6 3.2.3操作可行性: 6 3.3 项目设计目标与原则 6 3.4系统流程分析 7 3.4.1操作流程 7 3.4.2添加信息流程 8 3.4.3删除信息流程 9 第4章 系统设计 11 …...
数据集划分——train_test_split函数使用说明
当我们拿到数据集时,首先需要对数据集进行划分训练集和测试集,sklearn提供了相应的函数供我们使用 一、讲解 快速随机划分数据集,可自定义比例进行划分训练集和测试集 二、官网API 官网API sklearn.model_selection.train_test_split(*a…...
Pytorch中关于forward函数的理解与用法
目录 前言1. 问题所示2. 原理分析2.1 forward函数理解2.2 forward函数用法 前言 深入深度学习框架的代码,发现forward函数没有被显示调用 但代码确重写了forward函数,于是好奇是不是python的魔术方法作用 1. 问题所示 代码如下所示: cla…...
vite跨域proxy设置与开发、生产环境的接口配置,接口在生产环境下,还能使用proxy代理地址吗
文章目录 vite的proxy开发环境设置如果后端没有提供可以替换的/mis等可替换的后缀的处理办法接口如何区分.env.development开发和.env.production生产环境接口在生产环境下,还能使用proxy代理地址吗? vite的proxy开发环境设置 环境: vite 4…...
【嵌入式】使用MultiButton开源库驱动按键并控制多级界面切换
目录 一 背景说明 二 参考资料 三 MultiButton开源库移植 四 设计实现--驱动按键 五 设计实现--界面处理 一 背景说明 需要做一个通过不同按键控制多级界面切换以及界面动作的程序。 查阅相关资料,发现网上大多数的应用都比较繁琐,且对于多级界面的…...
【数据结构】树的概念理解和性质推导(保姆级详解,小白必看系列)
目录 一、前言 🍎 为什么要学习非线性结构 ---- 树(Tree) 💦 线性结构的优缺点 💦 优化方案 ----- 树(Tree) 💦 树的讲解流程 二、树的概念及结构 🍐 树的概念 &…...
融合之力:数字孪生、人工智能和数据分析的创新驱动
数字孪生、人工智能(AI)和数据分析是当今科技领域中的三个重要概念,它们之间存在着紧密的关联和互动,共同推动了许多领域的创新和发展。 一、概念 数字孪生是一种数字化的模拟技术,它通过复制现实世界中的物理实体、…...
Spring的注解开发-Spring配置类的开发
Bean配置类的注解开发 Component等注解替代了<bean>标签,但像<import>、<context:componentScan>等非<bean>标签怎样去使用注解去替代呢?定义一个配置类替代原有的xml配置文件,<bean>标签以外的标签ÿ…...
Linux系统编程系列之进程间通信-信号量组
一、什么是信号量组 信号量组是信号量的一种, 是system-V三种IPC对象之一,是进程间通信的一种方式。 二、信号量组的特性 信号量组不是用来传输数据的,而是作为“旗语”,用来协调各进程或者线程工作的。信号量组可以一次性在其内…...
centos 6使用yum安装软件
1. 执行以下命令,查看当前操作系统 CentOS 版本。 cat /etc/centos-release返回结果如下图所示,则说明当前操作系统版本为 CentOS 6.9。 2. 执行以下命令,编辑 CentOS-Base.repo 和CentOS-Epel.repo文件。 vim /etc/yum.repos.d/CentOS-Bas…...
maven无法下载时的解决方法——笔记
右键项目然后点击创建setting.xml(因为现在创建了,所以没显示了,可以直接点击打开setting.xml) 然后添加 <mirror><id>nexus-aliyun</id><mirrorOf>*,!jeecg,!jeecg-snapshots</mirrorOf><name…...
Java Spring Boot 开发框架
Spring Boot是一种基于Java编程语言的开发框架,它的目标是简化Java应用程序的开发过程。Spring Boot提供了一种快速、易于使用的方式来创建独立的、生产级别的Java应用程序。本文将介绍Spring Boot的特性、优势以及如何使用它来开发高效、可靠的应用程序。 一、简介…...
Pytorch学习记录-1-张量
1. 张量 (Tensor): 数学中指的是多维数组; torch.Tensor data: 被封装的 Tensor dtype: 张量的数据类型 shape: 张量的形状 device: 张量所在的设备,GPU/CPU requires_grad: 指示是否需要计算梯度 grad: data 的梯度 grad_fn: 创建 Tensor 的 Functio…...
C++初阶-list的底层
目录 1.std::list实现的所有代码 2.list的简单介绍 2.1实现list的类 2.2_list_iterator的实现 2.2.1_list_iterator实现的原因和好处 2.2.2_list_iterator实现 2.3_list_node的实现 2.3.1. 避免递归的模板依赖 2.3.2. 内存布局一致性 2.3.3. 类型安全的替代方案 2.3.…...
<6>-MySQL表的增删查改
目录 一,create(创建表) 二,retrieve(查询表) 1,select列 2,where条件 三,update(更新表) 四,delete(删除表…...
大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
Qt/C++开发监控GB28181系统/取流协议/同时支持udp/tcp被动/tcp主动
一、前言说明 在2011版本的gb28181协议中,拉取视频流只要求udp方式,从2016开始要求新增支持tcp被动和tcp主动两种方式,udp理论上会丢包的,所以实际使用过程可能会出现画面花屏的情况,而tcp肯定不丢包,起码…...
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする
日语学习-日语知识点小记-构建基础-JLPT-N4阶段(33):にする 1、前言(1)情况说明(2)工程师的信仰2、知识点(1) にする1,接续:名词+にする2,接续:疑问词+にする3,(A)は(B)にする。(2)復習:(1)复习句子(2)ために & ように(3)そう(4)にする3、…...
Appium+python自动化(十六)- ADB命令
简介 Android 调试桥(adb)是多种用途的工具,该工具可以帮助你你管理设备或模拟器 的状态。 adb ( Android Debug Bridge)是一个通用命令行工具,其允许您与模拟器实例或连接的 Android 设备进行通信。它可为各种设备操作提供便利,如安装和调试…...
Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例
使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件,常用于在两个集合之间进行数据转移,如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model:绑定右侧列表的值&…...
WordPress插件:AI多语言写作与智能配图、免费AI模型、SEO文章生成
厌倦手动写WordPress文章?AI自动生成,效率提升10倍! 支持多语言、自动配图、定时发布,让内容创作更轻松! AI内容生成 → 不想每天写文章?AI一键生成高质量内容!多语言支持 → 跨境电商必备&am…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
