Android composse 声明式UI,更简单的自定义
wptr33 2024-12-08 19:10 19 浏览
Android compose
声明式UI,更简单的自定义; 实时带交互的预览功能 Compose 并不是类似于Recyclerview的高级控件,而是直接抛弃了View,ViewGroup那套东西,从上到下鲁了一套全新的框架,直白点说就是它的渲染机制,布局机制,触摸算法,以及UI 具体写法全都是新的
Compose 实现了声明式UI,替代传统的命令是UI; 可能对于我们来说第一个问题就是什么是声明式?什么是命令式UI? 首先看一下声明式UI长什么样
Compose是用kotlin来写的,它的每一个控件都是一个函数调用; Text() Text()并不会创建对象,它不是一个构造函数,而是一个普通函数;为了辨识度,Compose函数开头都是大写;也就是一个Composable
有人会疑问Text()地层到底是什么,是个Textview吗? 其实并不是它是更下层Canvas那套东西, Compose各个组件都是独立的新实现
看完它的写法,看刚才的问题什么是声明式UI,它这么写怎么就声明式了,它和我们一直以来的写法有什么区别
<pre mdtype="fences" cid="n107" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="hello"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="world"
/>
</LinearLayout></pre>
这是传统写法,还需要在java中 findviewbyId获取控件对象,来进行操作; 其实对于布局来说,写法大同小异,但是为什么传统写法叫命令式,而Compose叫声明式
对于声明式来说,你只需要把控件声明出来,而不需要手动更新; 如左边对应的Textview 它对应的数据改变了,我们怎么去更改数据呢,首先是findviewbyId获取控件对象,然后setText来进行数据变更; 而如果用Compose呢,则不用更新,当数据改变时Compose会自动更新到页面上
这个自动订阅功能很容易使用,你只要在初始化时加上by mutableStateOf,剩下全由Compose自动搞定; 这个神奇的特性,是利用了kotlin的Property Delegation来实现的,也就说明了为什么Compose只能用kotlin来编写,因为使用到了kotlin的许多特性,而这些特性使用java 不能简单实现
这就是所谓的声明式UI,你只要声明页面是什么样子,不用手动去更新; 因为页面会自动更新,传统页面如果数据发生改变,要使用代码,给出详细步骤命令代码去更新,这就是所谓的命令式
传统的所谓命令式,并不在XML部分,而是在于java部分; java代码去指挥去命令控件去更新,这才是命令式含义所在,而Compose通过订阅机制来更新UI,不需要指挥控件去更新,所以是声明式; 它并不是语言角度的定义,也不是写法角度定义,而是功能角度
换句话说如果传统的XML能通过数据和页面进行关联,让页面自动更新,而不是让咱们去指定更新,那么它也是声明式; 声明式UI 是强大的功能,并不是优秀的代码风格
这里就会想到data binding,他们俩都是通过页面绑定数据,来进行自动更新,但是他们俩还是有关键区别的; data binding 通过数据更新只能是页面元素的值,而compose可以更新页面中的任何内容,包括结构
如:通过一个boolean来判断一个页面的元素展示,当你把变量的值改变后,这个元素会从页面中完全消失,像从来没出现过一样,而不是像设置Visibility这种方式从视觉上隐藏,这两种策略,看起来差不多; 但其实是种机制的改变,这种机制给页面带来的灵活性和性能的提升是非常大的
除了Android Compose外苹果的SwiftUi和Flutter 都是使用的是声明式。可见是一种趋势了*
在做页面开发时大家都知道尽量避免布局的嵌套,层级的增加会大幅度拖慢页面的加载,主要是因为各种layout的重复测量; 每增加一层都指数级增加页面的时长,而compose是不怕页面嵌套的,它从根源上解决了这个问题,它不允许重复测量
它的处理方式是先对子View进行一个粗略的测量,根据内容尺寸,粗略的估计子View的大小;固有尺寸测量 InTrinsic Measurement
就是在Compose里疯狂嵌套组件,和把组件放在同以层级它的性能是一样的
缺点:目前滑动列表场景,是比不过原生RecyclerView的,但是未来可期
Compose 编程思想
声明性编程范式
长期以来,Android 视图层次结构一直可以表示为界面微件树 由于应用的状态会因用户交互等因素而发生变化,因此界面层次结构需要进行更新以显示当前数据;最常见的界面更新方式是使用findViewById() 等函数遍历树,并通过调用 button.setText(String)、container.addChild(View) 或 img.setImageBitmap(Bitmap) 等方法更改节点。这些方法会改变微件的内部状态
手动操纵视图会提高出错的可能性; 如果一条数据在多个位置呈现,很容易忘记更新显示它的某个视图;此外,当两项更新以意外的方式发生冲突时,也很容易造成异常状态。例如,某项更新可能会尝试设置刚刚从界面中移除的节点的值;一般来说,软件维护复杂性会随着需要更新的视图数量而增长
在过去的几年中,整个行业已开始转向声明性界面模型,该模型大大简化了与构建和更新界面关联的工程设计;该技术的工作原理是在概念上 从头开始重新生成整个屏幕 ,然后仅执行必要的更改。此方法可避免手动更新有状态视图层次结构的复杂性
Compose 是一个声明性界面框架
重新生成整个屏幕所面临的一个难题是,在时间、计算能力和电池用量方面可能成本高昂。为了减轻这一成本,Compose 会智能地选择在任何给定时间需要重新绘制界面的哪些部分;这会对您设计界面组件的方式有一定影响,如重组中所述
简单的可组合函数
使用 Compose,您可以通过定义一组接受数据而发出界面元素的可组合函数来构建界面。一个简单的示例是 Greeting 微件,它接受 String 而发出一个显示问候消息的 Text 微件。
声明性范式转变
在许多面向对象的命令式界面工具包中,您可以通过实例化微件树来初始化界面; 您通常通过膨胀 XML 布局文件来实现此目的。每个微件都维护自己的内部状态,并且提供 getter 和 setter 方法,允许应用逻辑与微件进行交互
在 Compose 的声明性方法中,微件相对无状态,并且不提供 setter 或 getter 函数。实际上,微件不会以对象形式提供;您可以通过调用带有不同参数的同一可组合函数来更新界面; 这使得向架构模式(如 [ViewModel)提供状态变得很容易,如[应用架构指南中所述。然后,可组合项负责在每次可观察数据更新时将当前应用状态转换为界面
当用户与界面交互时,界面会发起 onClick 等事件。这些事件应通知应用逻辑,应用逻辑随后可以改变应用的状态; 当状态发生变化时,系统会使用新数据再次调用可组合函数。这会导致重新绘制界面元素,此过程称为“重组”
动态内容
由于可组合函数是用 Kotlin 而不是 XML 编写的,因此它们可以像其他任何 Kotlin 代码一样动态。例如,假设您想要构建一个界面,用来问候一些用户
状态和组合
由于 Compose 是声明式工具集,因此更新它的唯一方法是通过新参数调用同一可组合项,这些参数是界面状态的表现形式; 每当状态更新时,都会发生重组;因此,TextField 不会像在基于 XML 的命令式视图中那样自动更新;可组合项必须明确获知新状态,才能相应地进行更新
<pre mdtype="fences" cid="n142" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">fun HelloContent() {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Hello!",
modifier = Modifier.padding(bottom = 8.dp),
style = MaterialTheme.typography.h5
)
OutlinedTextField(
value = "",
onValueChange = { },
label = { Text("Name") }
)
}
}</pre>
如果运行此代码,您将不会看到任何反应; 这是因为,TextField 不会自行更新,但会在其 value 参数更改时更新;这是因 Compose 中组合和重组的工作原理造成的
可组合项中的状态
可组合函数可以使用 remember可组合项记住单个对象; 系统会在初始组合期间将由 remember 计算的值存储在组合中,并在重组期间返回存储的值;remember 既可用于存储可变对象,又可用于存储不可变对象。 mutableStateOf会创建可观察的 MutableState,后者是与 Compose 运行时集成的可观察类型
value 如有任何更改,系统会安排重组读取 value 的所有可组合函数。对于 ExpandingCard,每当 expanded 发生变化时,都会导致重组 ExpandingCard
在可组合项中声明 MutableState 对象的方法有三种:
● val mutableState = remember { mutableStateOf(default) }
● var value by remember { mutableStateOf(default) }
● val (value, setValue) = remember { mutableStateOf(default) }
布局
Column 竖向布局 Row 横向布局 Box 可将一个元素放在另一个元素上
如需在 Row 中设置子项的位置,请设置 horizontalArrangement 和 verticalAlignment 参数;对于 Column,请设置 verticalArrangement 和 horizontalAlignment 参数:
滚动修饰符
verticalScroll和horizontalScroll修饰符提供一种最简单的方法,可让用户在元素内容边界大于最大尺寸约束时滚动元素。利用 verticalScroll 和 horizontalScroll 修饰符,您无需转换或偏移内容
列表
系统会对所有列表项进行组合和布局,无论它们是否可见,因此如果您需要显示大量列表项(或长度未知的列表),则使用 Column等布局可能会导致性能问题
Compose 提供了一组组件,这些组件只会对在组件视口中可见的列表项进行组合和布局。这些组件包括 LazyColumn和 LazyRow
● 内容内边距 contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
● 内容外边距 verticalArrangement = Arrangement.spacedBy(4.dp)
修饰符
借助修饰符,您可以修饰或扩充可组合项;您可以使用修饰符来执行以下操作:
● 更改可组合项的大小、布局、行为和外观
● 添加信息,如无障碍标签
● 处理用户输入
● 添加高级互动,如使元素可点击、可滚动、可拖动或可缩放
修饰符是标准的 Kotlin 对象。您可以通过调用某个 Modifier类函数来创建修饰符;您可以将以下函数连在一起以将其组合起来:
修饰符顺序很重要 修饰符函数的顺序非常重要; 由于每个函数都会对上一个函数返回的 Modifier 进行更改,因此顺序会影响最终结果。让我们来看看这方面的一个示例:
<pre mdtype="fences" cid="n170" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Composable
fun ArtistCard(/*...*/) {
}</pre>
为什么我们需要一个新的UI 工具?
在Android中,UI工具包的历史可追溯到至少十几年前; 自那时以来,情况发生了很大变化,例如我们使用的设备,用户的期望,以及开发人员对他们所使用的开发工具和语言的期望
以上只是我们需要新UI工具的一个原因,另外一个重要的原因是 View.java 这个类实在是太大了,有太多的代 码,它大到你甚至无法在Githubs上查看该文件,因为它实际上包含了 30000 行代码,这很疯狂,而我们所使用的几 乎每一个Android UI 组件都需要继承于View
GogleAndroid团队的Anna-Chiara表示,他们对已经实现的一些API感到遗憾,因为他们也无法在不破坏功能的 情况下收回、修复或扩展这些API,因此现在是一个崭新起点的好时机。 这就是为什么Jetpack Compose 让我们看到了曙光
Jetpack Compose的着重点
包括一下几个方面:
● 加速开发
● 强大的UI工具
● 直观的Kotlin API
Compose API 的原则
Compose是一个声明式UI系统,其中,我们用一组函数来声明UI,并且一个Compose 函数可以嵌套另一个Compose函数,并以树的结构来构造所需要的UI; 在Compose中,我们称该树为UI 图,当UI需要改变的时候会刷新此UI图,比如Compose函数中有 if 语句,那 么Kotlin编译器就需要注意了
<pre mdtype="fences" cid="n186" lang="" class="md-fences md-end-block ty-contain-cm modeLoaded" spellcheck="false" style="box-sizing: border-box; overflow: visible; font-family: var(--monospace); font-size: 0.9em; display: block; break-inside: avoid; text-align: left; white-space: normal; background-image: inherit; background-position: inherit; background-size: inherit; background-repeat: inherit; background-attachment: inherit; background-origin: inherit; background-clip: inherit; background-color: rgb(248, 248, 248); position: relative !important; border: 1px solid rgb(231, 234, 237); border-radius: 3px; padding: 8px 4px 6px; margin-bottom: 15px; margin-top: 15px; width: inherit; color: rgb(51, 51, 51); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Composable
fun checkbox ( ... )
@Composable
fun TextView ( ... )
@Composable
fun Edittext ( ... )
@Composable
fun Image ( ... )
</pre>
在此过程中,Compose函数始终根据接收到的输入生成相同的UI,因此,放弃类结构不会有任何害处; 从类结构构建UI过渡到顶层函数构建UI对开发者和Android 团队都是一个巨大的转变,顶层函数还在讨论之中,还没有发布 release 版
组合优于继承
Jetpack Compose首选组合而不是继承,Compose会基于其他部分构建UI,但不会继承行为; 如果你经常关注Android或者对Android有所了解,你就会知道,Android中的几乎所有组件都继承于View类 (直接或间接继承)
比如 EidtText 继承于 TextView ,而同时 TextView 又继承于其他一些View,这样的继承机构 最终会指向跟 View 即 View.java ; 并且 View.java 又非常多的功能
而Compose团队则将整个系统从继承转移到了顶层函数; Textview , EditText , 复选框 和所有UI组件都是 它们自己的Compose函数,而它们构成了要创建UI的其他函数,代替了从另一个类继承
AndFix
AndFix是一个Android App的在线热补丁框架; 使用此框架,我们能够在不重复发版的情况下,在线修改App中的Bug;AndFix就是 “Android Hot-Fix”的缩写
原理是将有bug的方法B检查出来,修复这个方法B,通过apkpatch工具找出两个dex之间的区别生成.apatch文件 然后将补丁替换上去
就目前来说,AndFix支持Android 2.3到6.0版本,并且支持arm 与 X86系统架构的设备。完美支持Dalvik与ART的Runtime
AndFix 的补丁文件是以 .apatch 结尾的文件
如何使用?
直接添加 AndFix aar 到项目中的 编译依赖库中
maven 依赖:
Gradle依赖:
因为我使用的studio实现,暂时用的gradle依赖实现
把andfix依赖进来后,在我们的application中进行初始化
1、初始化PatchManager:
2、载入补丁包
越早的载入补丁包越好,通常来说,我们一般都会在自定义的Application中的onCreate方法里面载入补丁包;即:
3、添加补丁包
新的补丁包,下载后调用addPatch方法添加补丁;这个补丁会立即生效
接下来我来详细介绍下.apatch文件的生成
首先把压缩包解压出来,把我们有bug的线上包和已经修复好的apk包放到apkpatch文件夹下,然后把你的.jks apk签名文件也放进来,
接着,cmd运行命令行,进入我们当前的这个apkpatch-1.0.3文件夹下,运行apkapatch有详细介绍
第一个用法是生成.apatch文件的,第二个是合并.apatch文件的
注意:
每次产生的apatch文件用的名字不能相同的,相同会导致只有第一次的补丁能生效
与Nuwa对比
Nuwa是另一个热补丁框架,基于dex分包实现
与Nuwa相比,AndFix有以下特点:
● 不需要重启APP即可应用补丁
● 安全性更好
● 但是也有缺点:无法添加类和字段
尾述
私信发送 "底层源码“ 即可获取 完整代码 以及 更多Android学习笔记+源码解析+面试视频
技术是无止境的,你需要对自己提交的每一行代码、使用的每一个工具负责,不断挖掘其底层原理,才能使自己的技术升华到更高的层面
Android 架构师之路还很漫长,与君共勉
PS:有问题欢迎指正,可以在评论区留下你的建议和感受;
欢迎大家点赞评论,觉得内容可以的话,可以转发分享一下
- 上一篇:强烈推荐6款有助于提升效率办公APP
- 下一篇:android培训学习的大纲
相关推荐
- 抢先体验Windows 10 20H2新功能,该怎样操作呢?
-
Win10系统通常会在一年当中进行两次重大更新,分别于上下半年分别推出。上半年的更新主要针对功能的变化,而下半年则是提升系统稳定性。最近Win10下半年最重要的更新Windows1020H2已经开...
- 教程:如何关闭Win10/Win8.1/Win7管理共享
-
教程:如何关闭Win10/Win8.1/Win7管理共享出处:IT之家原创(晨风)默认情况下,Windows会创建一些隐藏的共享文件夹,这些文件夹在名称的末尾都有美元“$”标志。当用户在文件资源管理...
- Win11学院:如何强制让Windows 11设备蓝屏
-
IT之家12月15日消息,在Win11系统中蓝屏(BSoD)也称为“停止错误”(StopError)和“错误检查”(BugCheck),通常情况下只有在遇到关键问题的时候才会出现。显然...
- 微软承认Windows 10新BUG:错误显示没有网络连接
-
来源:cnBeta.COM在7月补丁星期二活动中,微软发布的累积更新已经修复Windows10系统中的大量BUG。不过近日,微软承认了存在于Windows10May2020(20H...
- 一课译词:双标(双标英文怎么写)
-
PhotobyMarkusSpiskeonUnsplash“双标[shuāngbiāo]”,网络流行语,完整说法是“双重标准”,翻译为“doublestandard”。“双标”是指“对同...
- 知识科普:USB端口如何禁用和解锁?
-
2015-07-3005:32:00作者:赵为民经常有人会说,我要保护我的笔记本电脑的USB端口,在未经授权的情况下不能够访问。是否有专业的软件可以将USB端口锁死,然后在需要的时候解锁呢?是的,...
- 小迈科技 X Hologres:高可用的百亿级广告实时数仓建设
-
通过本文,我们将会介绍小迈科技如何通过Hologres搭建高可用的实时数仓。一、业务介绍...
- Modbus-RTU通信(modbus rtu rtu over tcp)
-
通常情况下我们做Modbus通信的时候,都会先用测试软件进行测试,等通信测试通过之后,我们才会进行移植,我这边主要讲的是移植到PLC上,我现在这边还没有开始做PLC程序,那先把前期的用测试软件如何测...
- 警惕!利用Github进行水坑攻击安全风险通告
-
2022年5月19日,亚信安全CERT监测发现Github账户为rkxxz的用户发布了CVE-2022-26809和CVE-2022-24500的项目,项目内容介绍为:CVE-2022-26809...
- 手机越用越慢?小编教你如何用黑狱冰箱调教它!
-
看完智趣狗昨天推送的《看完秒懂!这就是Android手机越用越卡的原因!》一文后,我们不难知晓手机越用越慢多是体量更大的APP,以及APP之间相互唤醒而导致资源过度消耗引起的。所以,想让手机恢复高效率...
- 秒杀系统—3.第二版升级优化的技术文档一
-
大纲1.秒杀系统的服务细分和服务定位...
- Redis命令介绍(二十五)HSET & HSETNX
-
HSET将上送的键值对保存在key中存储的哈希表中。如果key不存在则创建一个新的哈希表。如果key已存在,则覆盖。在4.0版本后,HSET支持同时上送多键值对。...
- IDEA用上这十大插件就很舒服(intellij idea插件推荐)
-
本文翻译自国外论坛medium,原文地址:https://medium.com/@xjpp22/top-10-plugins-for-intellij-idea-you-dont-want-to-m...
-
- 常用 Git 命令清单(git常用命令速查表)
-
下面是整理的常用Git命令清单。几个专用名词的译名如下。...
-
2025-07-07 23:38 wptr33
- GitHub|清晰理解本地目录、暂存区、本地仓库、远程仓库的交互
-
GitHub是一个在线平台,旨在促进在一个共同项目上工作的个人之间的代码托管、版本控制和协作。通过该平台,无论何时何地,都可以对项目进行操作(托管和审查代码,管理项目和与世界各地的其他开发者共同开发...
- 一周热门
-
-
C# 13 和 .NET 9 全知道 :13 使用 ASP.NET Core 构建网站 (1)
-
因果推断Matching方式实现代码 因果推断模型
-
git pull命令使用实例 git pull--rebase
-
面试官:git pull是哪两个指令的组合?
-
git 执行pull错误如何撤销 git pull fail
-
git pull 和git fetch 命令分别有什么作用?二者有什么区别?
-
git fetch 和git pull 的异同 git中fetch和pull的区别
-
git pull 之后本地代码被覆盖 解决方案
-
还可以这样玩?Git基本原理及各种骚操作,涨知识了
-
git命令之pull git.pull
-
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)