降Compose十八掌之『见龙在田』| Modifier

news2024/11/20 18:44:11

公众号「稀有猿诉」        原文链接 降Compose十八掌之『见龙在田』| Modifier

通过前面的文章我们学会了如何使用元素来构建和填充我们的UI页面,但这只完成了一半,元素还需要装饰,以及进行动画和事件响应,这才能生成完整的UI。这就要用到Modifier,Jetpack Compose中的灵魂,它被用来装饰和增强Composables,让一个个平凡的元素变成鲜活的,好看的,可交互UI。我们来具体的看一下Modifier的使用方法。

对于Compose来说,每一个元素叫做Composable,它是一个函数,比如前面学过的布局(如Row)和小部件(如Text)等都是一个Composable,可以把它理解成为一个元素。

概念和基本使用方法

Modifier之于元素,犹如CSS之于HTML,但能做的更多,因为除了样式装饰以外,Modifier还能做很多事情,比如响应用户输入。

每一个Composable都可以接收一个Modifier参数,准确地说第一个参数都是Modifier,可以把一个Modifier对象作为第一个参数传给元素;也可以用命名参数如modifier = Modifier.padding(8.dp)。Modifier支持链式调用,它的每个方法都会把当前对象返回:

@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

装饰元素的样式

重点来看一下如何用Modifier装饰元素的样式。需要注意的是Modifier是装饰元素的共性样式如尺寸,背景,边框和位移等等,而像具体元素特性,如Text中的文本样式,是无法用Modifier来修改的。

尺寸Size

尺寸对于一个UI元素来说是必要的,就是渲染之后的视觉上的宽度和高度。可以通过Modifier的下列函数来进行尺寸约束:

  • width/height - 指定固定的偏好宽度和偏好高度,参数传入具体的数值如Modifier.width(320.dp).height(480.dp),就是指定某个元素的宽度是320dp,高度是480dp。偏好(preferred)的意思是可能会被其他约束条件覆盖,而最终值可能会不一样。
  • size - 同时指定宽度和高度为某一偏好的数值,只传一个参数就是一个正方形,如Modifier.size(100.dp),等同于Modifier.width(100.dp).height(100.dp);传两个参数时分别指定宽和高,如Modifier.size(320.dp, 480.dp)等同于Modifier.width(320.dp).height(480.dp)。
  • wrapContentWidth/wrapContentHeight/wrapContentSize - 让元素自己决定尺寸,无视最小尺寸限制(minimum_wdith/minimum_height),类似于XML中的wrap_content,这是比较严格的限制。
  • requiredWidth/requiredHeight - 指定宽度和高度必须为某一数值,不可以被其他限制约束覆盖。带有In的函数可以指定一个范围。
  • requiredSize - 指定宽度和高度必须为某一数值,带有In的函数可以指定范围。
  • widthIn/heightIn/sizeIn - 带有in的函数,可以指定一个范围而非具体数值,比如Modifier.widthIn(10.dp, 100.dp),就是说限制此元素的宽度为10dp到100dp之间。
  • fillMaxWidth/fillMaxHeight - 不固定具体的数值,按比例填充最大可用空间,比例为1.0时填满,类似于XML中的match_parent。
  • fillMaxSize - 按比例填充满可用空间。
  • weight - 权重比例,仅在父布局是Row或者Column时,且尺寸与父布局约束一致时有效,比如在Row中,对width生效,在Column中对height生效。最终的占比是『权重比例 x 可用空间』,不指定weight则weight是0,比如一个Row中,有三个元素,其中两个元素A和B指定了weight为1和2,另一个没指定,那么A将占Row中剩余可用宽度的1/3,B将占2/3。与View中的LinearLayout中的权重是差不多的。

Modifier修饰尺寸的函数比较多,容易学杂了,需要梳理一下:size相当于快捷方式,可以同时约束宽和高;带有In的函数可以为某个约束指定范围。宽高是一种限制性约束,不同的函数的限制严格性是不一样的,带有required是最严格的限制约束,优先级最高,如有冲突,以它为准;wrapContentWidth/wrapContentHeight/wrapContentSize是较严格的限制,仅次于required;width/height/size是中等严格,较wrapContent再次之;weight再次之;fillMax则是最弱的限制。

间隔

间隔(padding)是在元素与其边界之间添加的额外的空白空间,Modifier.padding不影响元素的尺寸,它是在测量之前就应用生效。

对于有XML经验的同学来说,以往的间隔有两个,一个是margin控制着元素边界之外的间隔,padding控制着边界与元素本身内容间隔。对于Modifier来说,只有一个函数,在不同的位置调用padding函数会有不同的效果,如果在尺寸之后调用padding,则是调整边界与内容之间的间隔,如果是在尺寸之前调用,则是调整边界与外部的间隔。另外,就是padding不可以传负值。

@Composable
fun PaddingDemo(modifier: Modifier = Modifier.fillMaxSize()) {
    Box(modifier = Modifier
        .fillMaxSize()
        .background(Color.LightGray)) {
        Text(
            text = "降龙十八掌",
            style = MaterialTheme.typography.headlineLarge,
            modifier = Modifier
                .padding(16.dp) // As margin: outside space beyond border
                .background(Color.Cyan)
                .size(360.dp, 120.dp)
                .padding(10.dp) // As padding: space between border and content
        )
    }
}

padding_demo.png

位移Offset

函数Modifier.offset用来给水平和垂直方向加一个位移,数值可正可负,注意仅是增加位移,并不会改变元素的尺寸。与View中的translateX和translateY是类似的。它有两个函数,只有一个参数时是水平和垂直方向都加上相同的位移;两个参数时是分别指定水平方向和垂直方向。

@Composable
fun ModifierDemo(modifier: Modifier = Modifier.fillMaxSize()) {
    var offset by remember { mutableStateOf(0) }
    Column(
        modifier = modifier
            .padding(8.dp)
            .clickable { offset += 8 }
    ) {
        Text(
            text = "降龙十八掌",
            style = MaterialTheme.typography.headlineLarge,
            color = MaterialTheme.colorScheme.primary,
            modifier = Modifier
                .offset(offset.dp, offset.dp)
                .background(Color.Cyan)
                .padding(16.dp)
        )
    }
}

offset_demo.gif

因为offset并不改变元素的尺寸,仅是在原位置上进行偏移,所以多用于点击效果,或者点击动画。

背景Background

使用Modifier.background函数来修改元素的背景颜色,唯一需要注意的是padding的影响,background是给尺寸所指定的区域加背景色,所以padding调用的位置会有影响。

边框Border

用Modifier.bodrer函数可以指定边框的样式,如形状,粗细,线条和颜色。需要注意的是它也是跟尺寸一样的,受padding的影响:

@Composable
fun BorderWithShape(modifier: Modifier = Modifier.fillMaxSize()) {
    Box {
        Text(
            text = "降龙十八掌",
            style = MaterialTheme.typography.headlineLarge,
            modifier = Modifier
                .padding(10.dp)
                .border(2.dp, SolidColor(Color.Green), RoundedCornerShape(20.dp))
                .padding(10.dp)
        )
    }
}

border_demo.png

变幻

Modifier还能对元素进行一些变幻,如透明度(Alpha),旋转(Rotate)和缩放(Scale)。通常用来实现一些非动画的静态特效。

Modifier.alpha指定透明度,0是透明,1是完全不透明,默认值是1.0。

Modifier.rotate实现旋转,参数是一个角度,顺时针旋转为正值,逆时针旋转为负值,默认值是0度。

Modifier.scale是以元素的几何中心为中心点进行缩放负值会进行水平和垂直方向翻转:

@Composable
fun TransformationDemo(modifier: Modifier = Modifier.fillMaxSize()) {
    Box(
        modifier = Modifier.fillMaxSize()
            .background(Color.LightGray)
    ) {
        Text(
            text = "降龙十八掌",
            style = MaterialTheme.typography.headlineLarge,
            color = MaterialTheme.colorScheme.primary,
            modifier = Modifier
                .padding(16.dp)
                .alpha(0.618f)
                .rotate(11.8f)
                .scale(0.618f)
                .background(Color.Cyan)
                .size(360.dp, 120.dp)
                .offset(10.dp, 10.dp)
        )
    }
}

transformation_demo.png

事件监听

Modifier.clickable函数用来指定点击事件响应。此外,还可以用于指定元素是否可以点击。

另外,还可以通过Modifier.scrollable来指定元素是否可以滑动。

最佳实践

Modifier是非常强大的,也是非常复杂的,前面列出的都是最为常用的一些函数。接下再来学习一下使用Modifier时需要注意的事项。

顺序很重要

Modifier有很多很多函数,修改着同一个对象实例,有些函数会相互影响,因此这些函数的调用顺序就变得相当重要,特别是涉及尺寸强相关的装饰特性时,如background和border,它们会受到padding以及变幻的影响。比如前面变幻小节的例子,可以试着修改函数的调用顺序,就会发现结果会不一样。在实例使用时,如果出现与预期不一致的结果时,就尝试调整一下Modifiier函数的顺序,看是否是顺序 导致的。

留意上下文

在Compose中,有一些Modifier函数只能在特定的元素中使用,这就涉及了Compose上下文(Scope)。比如说像Modifier.align只能在BoxScope中使用,也就是说只能在Box的子元素中使用。对其他任何布局来说都是不能用的,所以在使用的时候也要注意元素所在的父布局。

尽可能的复用

每个元素在渲染的时候都需要一个Modifier对象,通常情况下都是通过Modifier的函数进行对象创建。但对于一些循环的场景,且Modifier对象没啥变化 时,这时就应该复用对象,而非每次都创建。比如说动画,以及像集合性布局的子布局,这时都应该在其父布局缓存Modifier对象,直接传给子元素,而不是让其每次都创建新对象:

val modifier = Modifier
        .padding(12.dp)
        .background(Color.Gray)
@Composable
fun LoadingWheelAnimation() {
    val animatedState = animateFloatAsState(/*...*/)

    LoadingWheel(
        // 把Modifier对象缓存到上一级的父布局中,以免每帧动画都创建一个Modifier对象
        modifier = reusableModifier,
        animatedState = animatedState
    )
}

对于集合性布局也最好是能复用Modifier对象:

val reusableItemModifier = Modifier
    .padding(bottom = 12.dp)
    .size(216.dp)
    .clip(CircleShape)

@Composable
private fun AuthorList(authors: List<Author>) {
    LazyColumn {
        items(authors) {
            AsyncImage(
                // 提升到上一级中时进行缓存,而不是每次都创建
                modifier = reusableItemModifier,
            )
        }
    }
}

保持一致性

一致性对于代码的可维护性和可扩展性是非常重要的,因为每个元素都需要Modifier对象,在Compose中到处都可以看到Modifier,在实际使用中保持一致性就非常重要。比如说Modifier要作为Composable的第一个参数,参数的命名应该是modifier,并且最好要有默认值,可以查阅Compose本身的代码,可以发现其所有的元素都遵循此约定。另外,就是对于共性的装饰要提升到父布局中进行统一设定,比如说根布局统一设定padding,而不是每个子布局进行分别设定,等等。

参考资料

  • Compose modifiers
  • 6. Jetpack Compose Modifiers
  • 6. Using Compose Modifiers

subscription

欢迎搜索并关注 公众号「稀有猿诉」 获取更多的优质文章!

保护原创,请勿转载!

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

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

相关文章

遍历请求后端数据引出的数组forEach异步操作的坑

有一个列表数据&#xff0c;每项数据里有一个额外的字段需要去调另外一个接口才能拿到&#xff0c;后端有现有的这2个接口&#xff0c;现在临时需要前端显示出来&#xff0c;所以这里需要前端先去调列表数据的接口拿到列表数据&#xff0c;然后再遍历请求另外一个接口去拿到对应…

C++|异常

目录 一、异常概念 二、异常使用 2.1异常的抛出与捕获 2.2异常的重新抛出 2.3异常安全注意事项 2.4异常规范 三、自定义异常体系 四、C标准库的异常体系 五、异常的优缺点 对于传统的错误处理机制&#xff0c;例如c语言常用的&#xff1a; 1.assert&#xff0c;捕获到…

社区6月月报 | Apache DolphinScheduler重要修复和优化记录

各位热爱Apache DolphinScheduler的小伙伴们&#xff0c;社区6月月报更新啦&#xff01;这里将记录Apache DolphinScheduler社区每月的重要更新&#xff0c;欢迎关注。 月度Merge Stars 感谢以下小伙伴上个月为Apache DolphinScheduler所做的精彩贡献&#xff08;排名不分先后…

过滤器与拦截器区别、应用场景介绍

我们在进行 Web 应用开发时&#xff0c;时常需要对请求进行拦截或处理&#xff0c;故 Spring 为我们提供了过滤器和拦截器来应对这种情况。 那么两者之间有什么不同呢&#xff1f;本文将详细讲解两者的区别和对应的使用场景。 过滤器 过滤器是一种在 Java Web 应用中用于处理…

2024-7-9 Windows NDK,Clion,C4droid 编译环境配置(基础|使用命令编译,非AndroidStudio),小白(记录)友好型教程

2024-7-9 Windows NDK,Clion,C4droid 编译环境配置(基础|使用命令编译),小白友好型 一直想使用NDK编译出lua库,然后进行开发.结果一直不成功,问题Bug出现了一堆(主要还是自己太菜,毕竟咱是编程散修一名>_<) NDK之前一直不会配置(直接用命令配置的那种,非AndroidStudio),一…

nvm下载

nvm下载 1.下载nvm安装包2.安装nvm3.修改settings.txt4.安装成功5.继续配置 下载nvm之前,你最好将你电脑上的node卸载掉,直接在winx中卸载就行 1.下载nvm安装包 https://github.com/coreybutler/nvm-windows/releases 2.安装nvm 3.修改settings.txt root: E:\nvm\install\nv…

哦华为仓颉语言

本来我不太想说的&#xff0c;奈何有不少粉丝提问提到了这语言&#xff0c;目前的情况我不透露太多&#xff0c;看过这课程C实现一门计算机编程语言到手撸虚拟机实战的懂的自然懂。 在互联网领域几乎大部分应用软件运行在X86 LINUX上居多&#xff0c;如果你有问题可以先学习这…

红酒的秘密配方:如何调配出个性化的口感?

在红酒的世界里&#xff0c;每一滴都蕴藏着大自然的秘密和酿酒师的匠心。那些令人陶醉的口感、迷人的色泽和香气&#xff0c;都是经过精心调配和时光酝酿的结果。今天&#xff0c;就让我们一起揭开红酒调配的神秘面纱&#xff0c;探索如何调配出个性化的口感&#xff0c;感受雷…

大语言模型垂直化训练技术与应用

在人工智能领域&#xff0c;大语言模型&#xff08;Large Language Models, LLMs&#xff09;已经成为推动技术进步的关键力量&#xff0c;垂直化训练技术逐渐成为研究的热点&#xff0c;它使得大模型能够更精准地服务于特定行业和应用场景。本文结合达观数据的分享&#xff0c…

C++·模板进阶

1. 非类型模板参数 之前我们写的模板参数都设定class类型的&#xff0c;这个模板参数用来给下面的代码中的某些元素定义类型&#xff0c;我们管这种模板参数叫类型形参。非类型模板参数就是用一个常量作为模板的一个参数&#xff0c;在模板中可将该参数当作常量来使用&#xff…

RT2-使用NLP的方式去训练机器人控制器

目标 研究在网络数据上训练的视觉语言模型也可以直接结合到端到端的机器人控制中&#xff0c;提升泛化性以及获得突出的语义推理&#xff1b;使得单个的端到端训练模型可以同时学习从机器人观测到动作的映射&#xff0c;这个过程可以受益于基于网络上的语言和视觉语言数据的预训…

Python函数 之 函数基础

print() 在控制台输出 input() 获取控制台输⼊的内容 type() 获取变量的数据类型 len() 获取容器的⻓度 (元素的个数) range() ⽣成⼀个序列[0, n) 以上都是我们学过的函数&#xff0c;函数可以实现⼀个特定的功能。我们将学习⾃⼰如何定义函数, 实现特定的功能。 1.函数是什么…

C++进阶:继承和多态

文章目录 ❤️继承&#x1fa77;继承与友元&#x1f9e1;继承和静态成员&#x1f49b;菱形继承及菱形虚拟继承&#x1f49a;继承和组合 ❤️多态&#x1fa77;什么是多态&#xff1f;&#x1f9e1;多态的定义以及实现&#x1f49b;虚函数&#x1f49a;虚函数的重写&#x1f499…

如何借助社交媒体影响者的力量,让品牌影响力倍增?

一、引言&#xff1a;为何社交媒体影响者如此关键&#xff1f; 在信息爆炸的今天&#xff0c;社交媒体已成为塑造消费者行为与品牌认知的重要渠道。社交媒体影响者&#xff0c;凭借其在特定领域的专业知识、庞大的粉丝基础及高度的互动性&#xff0c;成为了品牌传播不可忽视的…

JVM原理(二四):JVM虚拟机锁优化

高效并发是从JDK 5升级到JDK 6后一项重要的改进项&#xff0c;HotSpot虛 拟机开发团队在这个版本上花费了大量的资源去实现各种锁优化技术&#xff0c;如适应性自旋( Adaptive Spinning)、锁消除( Lock Elimination)、锁膨胀(Lock Coarsening)、轻量级锁(Lightweight Locking)、…

DeepMind的JEST技术:AI训练速度提升13倍,能效增强10倍,引领绿色AI革命

谷歌旗下的人工智能研究实验室DeepMind发布了一项关于人工智能模型训练的新研究成果&#xff0c;声称其新提出的“联合示例选择”&#xff08;Joint Example Selection&#xff0c;简称JEST&#xff09;技术能够极大地提高训练速度和能源效率&#xff0c;相比其他方法&#xff…

数字信号处理教程(3)——z变换

在连续时间域中的每一种分析方法&#xff0c;在离散时间域中想必也能得到对应一种分析方法。连续傅里叶变换对应着离散傅里叶变换&#xff08;DFT&#xff09;&#xff0c;而在拉普拉斯变换则是对应着z变换。z变换能够将信号表示成离散复指数函数的线性组合。连续傅里叶变换可以…

NAT技术及其应用

网络地址转换&#xff08;NAT&#xff0c;Network Address Translation&#xff09;是一种广泛应用于现代网络中的技术&#xff0c;旨在解决IP地址短缺问题&#xff0c;同时增强网络的安全性和灵活性。本文将详细解释NAT技术的工作原理&#xff0c;并探讨其在家庭及企业网络中的…

华为HCIP Datacom H12-821 卷30

1.单选题 以下关于OSPF协议报文说法错误的是? A、OSPF报文采用UDP报文封装并且端口号是89 B、OSPF所有报文的头部格式相同 C、OSPF协议使用五种报文完成路由信息的传递 D、OSPF所有报文头部都携带了Router-ID字段 正确答案&#xff1a;A 解析&#xff1a; OSPF用IP报…

【大模型LLM面试合集】大语言模型架构_layer_normalization

2.layer_normalization 1.Normalization 1.1 Batch Norm 为什么要进行BN呢&#xff1f; 在深度神经网络训练的过程中&#xff0c;通常以输入网络的每一个mini-batch进行训练&#xff0c;这样每个batch具有不同的分布&#xff0c;使模型训练起来特别困难。Internal Covariat…