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

公众号「稀有猿诉」        原文链接 降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.mzph.cn/diannao/43877.shtml

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

相关文章

2.5章节python中布尔类型

在Python中&#xff0c;布尔类型&#xff08;Boolean type&#xff09;用于表示真&#xff08;True&#xff09;或假&#xff08;False&#xff09;的值。这是编程中非常基础且重要的一个概念&#xff0c;因为它允许程序进行条件判断&#xff0c;从而根据条件执行不同的代码块。…

智慧校园行政办公-红头文件功能概述

在智慧校园的行政办公系统中&#xff0c;红头文件的管理功能是一项重要的组成部分&#xff0c;它极大地提升了文件处理的效率与规范性。该功能围绕文件的创建、审批、归档等关键环节&#xff0c;进行了全面的数字化改造。 首先&#xff0c;系统内置了多种标准化的红头文件模板&…

一文实践强化学习训练游戏ai--doom枪战游戏实践

一文实践强化学习训练游戏ai–doom枪战游戏实践 上次文章写道下载doom的环境并尝试了简单的操作&#xff0c;这次让我们来进行对象化和训练、验证&#xff0c;如果你有基础&#xff0c;可以直接阅读本文&#xff0c;不然请你先阅读Doom基础知识&#xff0c;其中包含了下载、动作…

打开ps提示dll文件丢失如何解决?教你几种靠谱的方法

在日常使用电脑过程中&#xff0c;由于不当操作&#xff0c;dll文件丢失是一种常见现象。当dll文件丢失时&#xff0c;程序将无法正常运行&#xff0c;比如ps&#xff0c;pr等待软件。此时&#xff0c;我们需要对其进行修复以恢复其功能&#xff0c;下面我们一起来了解一下出现…

【堆 (优先队列) 扫描线】218. 天际线问题

本文涉及知识点 堆 &#xff08;优先队列) 扫描线 LeetCode218. 天际线问题 城市的 天际线 是从远处观看该城市中所有建筑物形成的轮廓的外部轮廓。给你所有建筑物的位置和高度&#xff0c;请返回 由这些建筑物形成的 天际线 。 每个建筑物的几何信息由数组 buildings 表示&…

景芯SoC训练营DFT debug

景芯训练营VIP学员在实践课上遇到个DFT C1 violation&#xff0c;导致check_design_rule无法通过&#xff0c;具体报错如下&#xff1a; 遇到这个问题第一反映一定是确认时钟&#xff0c;于是小编让学员去排查add_clock是否指定了时钟&#xff0c;指定的时钟位置是否正确。 景芯…

2024年信息系统项目管理师1批次上午客观题参考答案及解析(3)

51、探索各种选项&#xff0c;权衡包括时间与成本、质量与成本、风险与进度、进度与质量等多种因素&#xff0c;在整个过程中&#xff0c;舍弃无效或次优的替代方案&#xff0c;这种不确定性应对方法是()。 A&#xff0e;集合设计 B&#xff0e;坚韧性 C&#xff0e;多种结果…

离线运行Llama3:本地部署终极指南_liama2 本地部署

4月18日&#xff0c;Meta在官方博客官宣了Llama3&#xff0c;标志着人工智能领域迈向了一个重要的飞跃。经过笔者的个人体验&#xff0c;Llama3 8B效果已经超越GPT-3.5&#xff0c;最为重要的是&#xff0c;Llama3是开源的&#xff0c;我们可以自己部署&#xff01; 本文和大家…

大数据------JavaWeb------FilterListenerAJAXAxiosJSON

Filter Filter简介 定义&#xff1a;Filter表示过滤器&#xff0c;是JavaWeb三大组件&#xff08;Servlet、Filter、Listener&#xff09;之一。 作用&#xff1a;它可把对资源&#xff08;Servlet、JSP、Html&#xff09;的请求拦截下来从而实现一些特殊功能 过滤器一般完成…

【QT中实现摄像头播放、以及视频录制】

学习分享 1、效果图2、camerathread.h3、camerathread.cpp4、mainwindow.h5、mainwindow.cpp6、main.cpp 1、效果图 2、camerathread.h #ifndef CAMERATHREAD_H #define CAMERATHREAD_H#include <QObject> #include <QThread> #include <QDebug> #include &…

选择排序(C语言版)

选择排序是一种简单直观的排序算法 算法实现 首先在未排序序列中找到最小&#xff08;大&#xff09;元素&#xff0c;存放到排序序列的起始位置。 再从剩余未排序元素中继续寻找最小&#xff08;大&#xff09;元素&#xff0c;然后放到已排序序列的末尾。 重复第二步&…

020-GeoGebra中级篇-几何对象之点与向量

本文概述了在GeoGebra中如何使用笛卡尔或极坐标系输入点和向量。用户可以通过指令栏输入数字和角度&#xff0c;使用工具或指令创建点和向量。在笛卡尔坐标系中&#xff0c;示例如“P(1,0)”&#xff1b;在极坐标系中&#xff0c;示例如“P(1;0)”或“v(5;90)”。文章还介绍了点…

深入理解循环神经网络(RNN)

深入理解循环神经网络&#xff08;RNN&#xff09; 循环神经网络&#xff08;Recurrent Neural Network, RNN&#xff09;是一类专门处理序列数据的神经网络&#xff0c;广泛应用于自然语言处理、时间序列预测、语音识别等领域。本文将详细解释RNN的基本结构、工作原理以及其优…

uniapp本地打包到Android Studio生成APK文件

&#xff08;1&#xff09;安装 Android Studio 软件&#xff1b; 下载地址&#xff1a;官方下载地址&#xff0c;英文环境 安装&#xff1a;如下之外&#xff0c;其他一键 next &#xff08;2&#xff09;配置java环境&#xff1b; 下载&#xff1a;j…

基于SpringBoot构造超简易QQ邮件服务发送 第二版

目录 追加 邮箱附件 添加依赖 编码 测试 第二版的更新点是追加了 邮箱附件功能 ( 后期追加定时任务 ) 基于SpringBoot构造超简易QQ邮件服务发送(分离-图解-新手) 第一版 追加 邮箱附件 添加依赖 <!-- 电子邮件 --><dependency><groupId>org.spri…

如何评价Flutter?

哈喽&#xff0c;我是老刘 我们团队使用Flutter已经快6年了。 有很多人问过我们对Flutter的评价。 今天在这里回顾一下6年前选择Flutter时的原因&#xff0c;以及Flutter在这几年中的实际表现如何。 选择Flutter时的判断 1、性能 最开始吸引我们的就是其优秀的性能。 特别是…

【vue3|第16期】初探Vue-Router与现代网页路由

日期:2024年7月6日 作者:Commas 签名:(ง •_•)ง 积跬步以致千里,积小流以成江海…… 注释:如果您觉得有所帮助,帮忙点个赞,也可以关注我,我们一起成长;如果有不对的地方,还望各位大佬不吝赐教,谢谢^ - ^ 1.01365 = 37.7834;0.99365 = 0.0255 1.02365 = 1377.4083…

深入探索联邦学习框架 Flower

联邦学习框架 本文主要期望介绍一个设计良好的联邦学习框架 Flower&#xff0c;在开始介绍 Flower 框架的细节前&#xff0c;先了解下联邦学习框架的基础知识。 作为一个联邦学习框架&#xff0c;必然会包含对横向联邦学习的支持。横向联邦是指拥有类似数据的多方可以在不泄露…

【CVPR 2024】GART: Gaussian Articulated Template Models

【CVPR 2024】GART: Gaussian Articulated Template Models 一、前言Abstract1. Introduction2. Related Work3. Method3.1. Template Prior3.2. Shape Appearance Representation with GMM3.3. Motion Representation with Forward Skinning3.4. Reconstruct GART from Monocu…

Java--instanceof和类型转换

1.如图&#xff0c;Object&#xff0c;Person&#xff0c;Teacher&#xff0c;Student四类的关系已经写出来了&#xff0c;由于实例化的是Student类&#xff0c;因此&#xff0c;与Student类存在关系的类在使用instanceof时都会输出True&#xff0c;而无关的都会输出False&…