Jetpack Compose 架构层

news2024/11/19 3:50:25
点击查看:Jetpack Compose 架构层 官网

本页面简要介绍了组成 Jetpack Compose 的架构层,以及这种设计所依据的核心原则。

Jetpack Compose 不是一个单体式项目;它由一些模块构建而成,这些模块组合在一起,构成了一个完整的堆栈。通过了解组成 Jetpack Compose 的不同模块,您可以:

  • 使用适当的抽象级别来构建应用或库
  • 了解何时可以“降级”到较低级别,以获取更多的控制权或更高的自定义程度
  • 尽可能减少依赖项

Jetpack Compose 的主要层包括:
在这里插入图片描述
图 1. Jetpack Compose 的主要层。
每一层均基于较低的层逐级构建,并通过组合功能来创建更高级别的组件。每一层都是基于较低层的公共 API 构建的,用于验证模块边界,还支持您根据需要替换任何层。让我们自下而上地分析这些层。

运行时
此模块提供了 Compose 运行时的基本组件,例如 remember、mutableStateOf、@Composable 注释和 SideEffect。如果您只需要 Compose 的树管理功能,而不需要其界面,则可以考虑直接基于此层进行构建。

界面
界面层由多个模块(ui-text、ui-graphics 和 ui-tooling 等)组成。这些模块实现了界面工具包的基本组件,例如 LayoutNode、Modifier、输入处理程序、自定义布局和绘图。如果您只需要用到界面工具包的基本概念,则可以考虑基于此层进行构建。

基础
此模块为 Compose 界面提供了与设计系统无关的构建块,例如 Row 和 Column、LazyColumn、特定手势的识别等。您可以考虑基于基础层构建自己的设计系统。

Material
此模块为 Compose 界面提供了 Material Design 系统的实现,同时提供了一个主题系统以及若干样式化组件、涟漪效果指示元素和图标。在您的应用中使用 Material Design 时,不妨基于此层进行构建。

设计原则

Jetpack Compose 的一个指导原则是提供可以组合在一起的重点突出的小块功能片段,而不是几个单体式组件。这种方法有许多优点。

控制

更高级别的组件往往能完成更多操作,但您拥有的直接控制权较少。如果您需要更多控制权,可以“下拉”使用较低级别的组件。

例如,如果您想为某个组件的颜色添加动画效果,可以使用 animateColorAsState API:

val color = animateColorAsState(if (condition) Color.Green else Color.Red)

不过,如果您需要组件始终以灰色开头,则不能使用此 API。您可以改为下拉菜单使用较低级别的 Animatable API:

val color = remember { Animatable(Color.Gray) }
LaunchedEffect(condition) {
    color.animateTo(if (condition) Color.Green else Color.Red)
}

较高级别的 animateColorAsState API 本身基于较低级别的 Animatable API 构建而成。使用较低级别的 API 的过程更为复杂,但可提供更多的控制权。请选择最符合您需求的抽象化级别。

自定义

通过将较小的构建块组合成更高级别的组件,可大幅降低按需自定义组件的难度。例如,可以考虑使用 Material 层提供的 Button 的实现:

@Composable
fun Button(
    // …
    content: @Composable RowScope.() -> Unit
) {
    Surface(/* … */) {
        CompositionLocalProvider(/* … */) { // set LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                Row(
                    // …
                    content = content
                )
            }
        }
    }
}

Button 由 4 个组件组合而成:

  1. Material Surface:用于提供背景、形状和点击处理方式等。
  2. CompositionLocalProvider:用于在启用或停用相应按钮时更改内容的 Alpha 值
  3. ProvideTextStyle:用于设置要使用的默认文本样式
  4. Row:用于为相应按钮的内容提供默认布局政策

为了使结构更加清晰,我们省略了一些参数和注释,但整个组件只有 40 行左右的代码,因为它只是组合了这 4 个组件来实现该按钮。Button 等组件会自行判断它们需要公开哪些参数,同时在实现常见的自定义项和可能使组件更难使用的参数突增之间创造平衡。例如,Material 组件可提供 Material Design 系统中指定的自定义项,这样可以轻松遵循 Material Design 原则。

不过,如果您希望在组件参数之外进行自定义,则可以“降级”某个级别并复刻某个组件。例如,Material Design 指定按钮应具有纯色背景。如果您需要渐变背景,Button 参数就不适用了,因为它不支持此选项。在此类情况下,您可以将 Material Button 实现作为参考,并构建您自己的组件:

@Composable
fun GradientButton(
    // …
    background: List<Color>,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(
                Brush.horizontalGradient(background)
            )
    ) {
        CompositionLocalProvider(/* … */) { // set material LocalContentAlpha
            ProvideTextStyle(MaterialTheme.typography.button) {
                content()
            }
        }
    }
}

上述实现继续使用 Material 层中的组件,例如 Material 的当前内容 Alpha 值和当前文本样式的概念。不过,它会将 Material Surface 替换为 Row 并设置其样式,以获得所需的外观。

注意:当“降级”到较低层以自定义组件时,请确保不会因忽视无障碍功能支持等原因而使任何功能发生降级。您要为哪个组件创建分支,就应以哪个组件作为指导。

如果您根本不想使用 Material 概念(例如,如果您要构建自己的定制设计系统),则可以选择仅使用基础层组件:

@Composable
fun BespokeButton(
    // …
    backgroundColor: Color,
    modifier: Modifier = Modifier,
    content: @Composable RowScope.() -> Unit
) {
    Row(
        // …
        modifier = modifier
            .clickable(onClick = {})
            .background(backgroundColor)
    ) {
        // No Material components used
        content()
    }
}

Jetpack Compose 为最高级别的组件保留了最为简洁的名称。例如,androidx.compose.material.Text 基于 androidx.compose.foundation.text.BasicText 构建。这样一来,如果您想替换更高级别,则可以为自己的实现提供更易于发现的名称。

注意:为组件创建分支意味着,您不会从上游组件的任何未来增补项或 bug 修复中受益。

选择合适的抽象化级别

Compose 以构建可重复使用的分层组件作为理念,这意味着您不应该始终以构建较低级别的构建块为目标。许多较高级别的组件不仅能够提供更多功能,而且通常还会融入最佳实践,例如支持无障碍功能等。

例如,如果您想为自己的自定义组件添加手势支持,可以使用 Modifier.pointerInput 从头开始构建;但在此之上还有其他更高级别的组件,它们可以提供更好的起点,例如 Modifier.draggable、Modifier.scrollable 或 Modifier.swipeable。

一般来讲,最好基于能提供您所需功能的最高级别的组件进行构建,以便从其包含的最佳实践中受益。

了解详情

如需查看构建自定义设计系统的示例,请参阅 Jetsnack 示例。

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

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

相关文章

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的人脸表情识别系统(附完整资源+PySide6界面+训练代码)

摘要&#xff1a;本篇博客呈现了一种基于深度学习的人脸表情识别系统&#xff0c;并详细展示了其实现代码。系统采纳了领先的YOLOv8算法&#xff0c;并与YOLOv7、YOLOv6、YOLOv5等早期版本进行了比较&#xff0c;展示了其在图像、视频、实时视频流及批量文件中识别人脸表情的高…

【elementUi-table表格】 滚动条 新增监听事件; 滚动条滑动到指定位置;

1、给滚动条增加监听 this.dom this.$refs.tableRef.bodyWrapperthis.dom.scrollTop 0let _that thisthis.dom.addEventListener(scroll, () > {//获取元素的滚动距离let scrollTop _that.dom.scrollTop//获取元素可视区域的高度let clientHeight this.dom.clientHeigh…

springboot+vue项目基础开发(17)路由使用

路由 在前端中,路由指的是根据不同的访问路径,展示不同的内容 vue Router的vue.js的官方路由 安装vue Router 再启动 在src下面新建router文件,创建index.js 代码 import {createRouter,createWebHashHistory} from vue-router //导入组件 import Login from @/views/Log…

SparkSQL学习03-数据读取与存储

文章目录 1 数据的加载1.1 方式一&#xff1a;spark.read.format1.1.1读取json数据1.1.2 读取jdbc数据 1.2 方式二&#xff1a;spark.read.xxx1.2.1 读取json数据1.2.2 读取csv数据1.2.3 读取txt数据1.2.4 读取parquet数据1.2.5 读取orc数据1.2.6 读取jdbc数据 2 数据的保存2.1…

RT-Thread-快速入门-2-时钟与定时器

时钟与定时器 阅读须知 定义与作用 定义 系统时钟 系统时钟在RT-Thread中用于管理时间&#xff0c;为系统运行提供时间基准。系统时钟由硬件计时器&#xff08;通常是CPU的内部定时器或外部定时器&#xff09;提供时钟节拍&#xff0c;这些时钟节拍通常以固定频率中断CPU&#…

opengl 学习纹理

一.纹理是什么&#xff1f; 纹理是一个2D图片&#xff08;甚至也有1D和3D的纹理&#xff09;&#xff0c;它可以用来添加物体的细节&#xff1b;类似于图像一样&#xff0c;纹理也可以被用来储存大量的数据&#xff0c;这些数据可以发送到着色器上。 采样是指用纹理坐标来获取纹…

npm install 失败,需要node 切换到 对应版本号

npm install 失败 原本node 的版本号是16.9&#xff0c;就会报以上错误 node版本问题了&#xff0c;我切到这个版本&#xff0c;报同样的错。降一下node&#xff08;14.18&#xff09;版本就好了 具体的方法&#xff1a;&#xff08;需要在项目根目录下切换&#xff09; 1. …

微服务学习

一、服务注册发现 服务注册就是维护一个登记簿&#xff0c;它管理系统内所有的服务地址。当新的服务启动后&#xff0c;它会向登记簿交待自己的地址信息。服务的依赖方直接向登记簿要Service Provider地址就行了。当下用于服务注册的工具非常多ZooKeeper&#xff0c;Consul&am…

JavaScript从零写网站《一瞬》开发日志20240223

产品介绍 一个无需注册能随时发布图片并配一段文字介绍的app&#xff0c;有时间线&#xff0c;用户在主页面向下滑动&#xff0c;可以看到被发布的若干图片&#xff0c;并且能够在每一个发布处做基本互动——评论&#xff0c;点赞 编程语言 本产品使用htmlcssJavaScript开发…

【Docker】构建pytest-playwright镜像并验证

Dockerfile FROM ubuntu LABEL maintainer "langhuang521l63.com" ENV TZAsia/Shanghai #设置时区 #安装python3依赖与下载安装包 RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone \&& apt update \&&…

Linux——进程概念

目录 冯诺依曼体系结构 操作系统 管理 系统调用和库函数 进程的概念 进程控制块——PCB 查看进程 通过系统调用获取进程标示符 通过系统调用创建进程 进程状态 运行状态-R ​编辑 浅度睡眠状态-S 深度睡眠状态-D 暂停状态-T 死亡状态-X 僵尸状态-Z 僵尸进程…

Open CASCADE学习|绘制砂轮

今天绘制一个砂轮&#xff0c;其轮廓由两条直线段和两段圆弧构成&#xff0c;圆弧分别与直线相切&#xff0c;两条圆弧之间相交而非相切。建模思路是&#xff1a;先给定两条直线段的起始点及长度&#xff0c;画出直线段&#xff0c;然后给定其中一圆弧的半径及圆心角&#xff0…

Linux之ACL访问控制列表

一、ACL权限的介绍 1.1 什么是ACL 访问控制列表&#xff08;ACL&#xff09;是一种网络安全技术&#xff0c;它通过在网络设备&#xff08;如路由器、交换机和防火墙&#xff09;上定义一系列规则&#xff0c;对进出接口的数据包进行控制。这些规则可以包含“允许”&…

解决IDEA中Maven下载依赖包过慢或报错的问题

由于公司项目迭代&#xff0c;越来越多的项目开始转型新版本&#xff0c;由于我对Java一直不感冒&#xff0c;但要顺应公司项目要求&#xff0c;遂自己要逐步开始完善Java相关的知识层面&#xff0c;此篇是我在学习SpringBoot时对一些不懂地方及遇到问题时的记录。 学习视频链…

Day 1.进程的基本概念、相关命令、函数结口

进程基本概念 一、进程&#xff1a; 程序&#xff1a;存放在外存中的一段数据组成的文件 进程&#xff1a;是一个程序动态执行的过程&#xff0c;包括进程的创建、进程的调度、进程的消亡 二、进程相关的命令 1.top 动态查看当前系统中所有的进程信息&#xff08;根据CPU…

基于PostGIS的慢查询引起的空间索引提升实践

目录 前言 一、问题定位 1、前端接口定位 2、后台应用定位 3、找到问题所在 二、空间索引优化 1、数据库查询 2、创建空间索引 3、geography索引 4、再看前端响应 总结 前言 这是一个真实的案例&#xff0c;也是一个新入门的工程师很容易忽略的点。往往在设计数据库的…

项目管理:如何成功完成一个项目

项目管理是一项重要的技能&#xff0c;它可以帮助你成功地完成一个项目。以下是一些关键的步骤&#xff0c;可以帮助你实现这一目标&#xff1a; 1. 明确项目目标&#xff1a;在开始项目之前&#xff0c;你需要明确项目的目标。这将有助于你制定一个明确的计划&#xff0c;并确…

最长公共前缀【简单】

题目 编写一个函数来查找字符串数组中的最长公共前缀。 如果不存在公共前缀&#xff0c;返回空字符串 ""。 示例如下&#xff1a; 所给提示如下&#xff1a; 1 < strs.length < 2000 < strs[i].length < 200strs[i] 仅由小写英文字母组成 解题 根据…

iOS面试:4.多线程GCD

一、多线程基础知识 1.1 什么是进程&#xff1f; 进程是指在系统中正在运行的一个应用程序。对于电脑而已&#xff0c;你打开一个软件&#xff0c;就相当于开启了一个进程。对于手机而已&#xff0c;你打开了一个APP&#xff0c;就相当于开启了一个进程。 1.2 什么是线程&am…

反序列化字符串逃逸 [安洵杯 2019]easy_serialize_php1

打开题目 $_SESSION是访客与整个网站交互过程中一直存在的公有变量 然后看extract()函数的功能&#xff1a; extract($_POST)就是将post的内容作为这个函数的参数。 extract() 函数从数组中将变量导入到当前的符号表(本题的作用是将_SESSION的两个函数变为post传参) function…