Modifier是Compose中的重要概念,能够让UI呈现更加专业、好看的视觉效果。

为什么使用Modifier?


常规的View体系中,控件以实例对象的形式存在,控件可以在实例化之后再动态配置属性,但是Composable本质上是函数,只能在调用的同时通过参数传递进行配置,如果没有Modifier,参数签名会变得很长(虽然Kotlin支持默认参数)。

使用Modiifer可以很好地解决这个问题,它就像Composable的配置文件,可以在此对Composable的样式和行为进行统一配置。

Modifier是一组有序的链式调用


Modifier通过链式调用“装饰”我们的Composable,为其添加BackgroundPaddingonClick事件等。

链上的每个操作符都创建一个Element,整个调用链是一组Element的有序执行单元

Text("Hello, World!",Modifier.padding(16.dp) // Outer padding; outside background.background(color = Color.Green) // Solid element background color.padding(16.dp) // Inner padding; inside background, around text
)

如上,调用链上的两个padding不是覆盖关系,而是按照顺序发挥作用。

padding创建PaddingModifier

class PaddingModifier(val start: Dp, val top : d'p...) : Modifier.Elementfun Modifier.padding(all: Dp) = this.then(PaddingModifier(start = all, top = all, xxx))

then用来组合两个Modifier并保持顺序执行。

open infix fun then(other: Modifier): Modifier

Modifier类似RxJava的Observable,基于函数式编程思想,将操作符串联成一组可执行函数,在Composable渲染的时候才执行。

使用Modifier装饰Composable


Modifier的操作符(API)虽然数量多,但是语义明确,上手不难。下面通过一个例子带大家体验一下如何使用Modifier装饰我们的Composable。

我们试着用Compose实现一个关注列表的Item,如下

@Composable
fun Plain() {Row(modifier = Modifier.fillMaxWidth()) {Image(modifier = Modifier.size(40.dp),bitmap = imageResource(id = R.drawable.miku),contentDescription = null, // decorative)Column(modifier = Modifier.weight(1f)) {Text(text = name, maxLines = 1)Text(text = desc, maxLines = 1)}Text("Follow", Modifier.padding(6.dp))}}

接下来,我们一步步通过Modifier对其进行装饰,在文章最后,UI将达到下面第二张图片的效果。

整体布局

Modifier.padding

我们使用RowColumn对Item内的元素进行了基本布局,但是元素之间缺少间距

Compose通过Modifier在Composable之间添加Padding

@Composable
fun Decorated() {Row(modifier = Modifier.fillMaxWidth().preferredHeightIn(min = 64.dp).padding(8.dp) //外间隙.border(1.dp, Color.LightGray, RoundedCornerShape(4.dp)).padding(8.dp) //内间隙) {...}
}

如上,我们对Item整体添加Padding。
border前后各有一个padding,分别表示对外和对内的间距。相对于传统布局有MarginPadding区分,Modifier中只有padding,根据调用链中的位置不同发挥不同作用,使用更加简单。

Modifier.border

border用来定义边框,RoundedCornerShape是一个Shape类型,用来指定边框的形状为圆角矩形。

我们还可以调用两次background来实现border的效果:

modifier = Modifier.background(Color.LightGray) .padding(1.dp) //两个backgound之间形成边框.background(Color.White)

Modifier.preferredHeight / Modifier.preferredHeightIn

preferedXXX等用来设置初始的size,例如preferedHeight可以设置Composable的默认高度,这个值可能被其他约束覆盖,若想要高度不被覆盖,就使用Modifier.height设置固定值

本例中使用preferedHeightIn,可以设置minHeightmaxHeight

Modifier.fillMaxWidth

fillMaxWidth表示填充整个父容器,相当于传统布局的match_parent

参数中传入Modifier

填充Row中的内容,从左往右依次是,头像、文字、按钮

@Composable
fun Decorated() {Row(modifier = Modifier.fillMaxWidth().preferredHeight(64.dp).padding(8.dp).border(1.dp, Color.LightGray, RoundedCornerShape(4.dp)).padding(8.dp)) {Avatar( //头像部分modifier = Modifier.padding(4.dp).align(Alignment.CenterVertically))Info( //文字部分Modifier.weight(1f).align(Alignment.CenterVertically))FollowBtn( //按钮Modifier.align(Alignment.CenterVertically))}
}

我们将具体实现抽成独立的Composable,在Row中调用并传入Modifier。

在Compose中定义Composable时,为Modifier预留参数位置是一个好习惯

Modifier为调用方提供了修改子元素样式的机会,但更重要的是有一些操作符只能在外部调用。

Modifier.align

Modifier的操作符都是扩展函数,并不是定义在一起。操作符定义在不同的空间中,可以限制某些操作符只能在特定父Comopsable中使用,避免误用。

interface RowScope {fun Modifier.align(alignment: Alignment.Vertical)
}

如上,align只能在Row中调用,用来设置子元素在垂直方向如何对齐。子元素不关心其在父容器中如何对齐,因此在外部设置align(Alignment.CenterVertically)后,传给子元素继续使用。

Modifier.weight

weight同样只能在Row中调用,为子元素分配在Row中的占比,类似于LinearLayoutlayout_weight。本例中让中间的文字部分占据所有所有空间

头像图片



我们对头像图片做圆形处理并添加边框,提升整体视觉效果。

@Composable
fun Avatar(modifier: Modifier) {Image(modifier = modifier.size(50.dp).clip(CircleShape).border(shape = CircleShape,border = BorderStroke(width = 2.dp,brush = Brush.linearGradient(colors = listOf(blue, teal200, green200, orange),start = Offset( 0f, 0f),end = Offset(100f,100f)))).border(shape = CircleShape,border = BorderStroke(4.dp, SolidColor(Color.White))),bitmap = imageResource(id = R.drawable.miku),contentDescription = null, // decorative)}

Modifier.size

首先size(50.dp) 设置图片的整体大小

Modifier.clip

clip用来将图片裁剪成指定形状,例子中clip(CircleShape)将图片裁剪成圆形

Modifier.border调用顺序

图片的边框由两部分组成,外层带颜色的部分,和内层的白色边框,因此调用链中出现了两个border()。两个border的调用顺序需要特备注意,border表示为后面的调用添加边框,所以在前面调用的后生效。所以例子中的border调用顺序如下:

Modifier
.border() //2dp 颜色边框
.border() //4dp 白色边框

BorderStroke & Brush

border使用BorderStroke填充边框颜色。

外边框使用Brush.linearGradient填充多种颜色组成的渐变色,startend表示颜色范围

BroderStroke(brush = Brush.linearGradient(colors = listOf(blue, teal200, green200, orange),start = Offset( 0f, 0f),end = Offset(100f,100f))
)

内边框使用SolidColor填充固定颜色

BorderStroke(brush = SolidColor(Color.White))

文字部分


@Composable
fun Info(modifier: Modifier) {Column(modifier = modifier) {Text(text = name,color = Color.Black,maxLines = 1,style = TextStyle(fontWeight = FontWeight.Bold,fontSize = 16.sp,letterSpacing = 0.15.sp))Text(text = desc,color = Color.Black.copy(alpha = 0.75f),maxLines = 1,style = TextStyle( // herefontWeight = FontWeight.Normal,fontSize = 14.sp,letterSpacing = 0.25.sp))}
}

许多字体的样式不借助Modifier,而是通过Text自身的属性以及TextStyle设置

文字颜色

color设置文字颜色,Compose的Color类功能强大, 例如这里可以设置透明度:Color.Black.copy(alpha = 0.75f)

TextStyle

TextStyle可以设置字体、字号等,例子中通过fontWeight设置了粗体

textDecoration

虽然本例中没有使用,但是Text还有一个重要属性textDecoration,对文字进行更有针对性的“装饰”,例如添加下划线、删除线等

textDecoration = TextDecoration.Underline

按钮



虽然有Compose提供了专门的Button实现按钮,使用Text同样可以实现按钮,而且可定制性更高。

@Composable
fun FollowBtn(modifier: Modifier) {val backgroundShape: Shape = RoundedCornerShape(4.dp)Text(text = "Follow",style = typography.body1.copy(color = Color.White),textAlign = TextAlign.Center,modifier =modifier.preferredWidth(80.dp).clickable(onClick = {}).shadow(3.dp, shape = backgroundShape).clip(backgroundShape).background(brush = Brush.verticalGradient(colors = listOf(Red500,orange700,),startY = 0f,endY = 80f)).padding(6.dp))
}

Modifier.background

为按钮添加了渐变的背景色以及阴影后,显得更加拟物、有质感
background中同样通过Brush添加渐变色

Modifier.shadow

shadow添加阴影,需要特别shadow在调用链中的位置,阴影本身也是占用面积的,所以要在background之前调用,避免阴影进入背景色区域中

Modifier.click

Text之所以可以替代Button实现按钮,是因为Modifier提供了click,可以让Composable处理onClick事件

最后



通过上面的例子,相信大家已经掌握了Modifier的基本使用方式,Modifier还有很多高级的API,后续有机会陆续分享给大家。Modifier在设计上吸取了装饰器模式、FP等多种编程思想,思路巧妙,值得大家学习。

更多API参考:

https://developer.android.com/reference/kotlin/androidx/compose/ui/Modifier

Jetpack Compose Modifier 使用入门相关推荐

  1. Jetpack Compose入门详解(实时更新)

    Jetpack Compose入门详解 前排提醒 前言(Compose是什么) 1.实战准备 一.优势与缺点 二.前四课 三.标准布局组件 1.Column 2.Row 3.Box 四.xml和com ...

  2. Jetpack Compose中的Modifier

    Modifier的基本使用 Modifier修饰符是Jetpack Compose中用来修饰组件的,提供常用的属性,写布局时几乎所有Composable组件的大部分属性都可以用Modifier 来修饰 ...

  3. Jetpack Compose 从入门到入门(六)

    本篇说说Compose中的Canvas. 1. Canvas @Composable fun Canvas(modifier: Modifier,onDraw: DrawScope.() -> ...

  4. 详解Jetpack Compose中的Modifier修饰符

    前言 本文将会介绍Jetpack Compose中的Modifier.在谷歌官方文档中它的描述是这么一句话:Modifier元素是一个有序.不可变的集合,它可以往Jetpack Compose UI元 ...

  5. Jetpack Compose 从入门到入门(三)

    本篇开始介绍Jetpack Compose 中的修饰符Modifier.修饰符可以用来执行以下操作: 更改可组合项的大小.布局.行为和外观. 添加信息,如无障碍标签. 处理用户输入. 添加高级互动,如 ...

  6. Android原生UI开发框架 《Jetpack Compose入门到精通》最全上手指南

    前言 在去年的Google/IO大会上,亮相了一个全新的 Android 原生 UI 开发框架-Jetpack Compose, 与苹果的SwiftIUI一样,Jetpack Compose是一个声明 ...

  7. 告别XML,Android新声明式UI框架《Jetpack Compose入门到精通》最全开发指南

    什么是Jetpack Compose? Jetpack Compose是Android的新声明式UI框架.长期以来, Android 开发人员习惯于使用带有状态视图的xml编写UI,这些状态视图通过逐 ...

  8. 《Jetpack Compose 从入门到实战》带你踏上 Compose 开发之旅~

    写书的契机 Jetpack Compose 首次亮相于 2019 年的 Google I/O 大会,彼时的我正在为抖音客户端研发一款基于原生视图渲染的声明式 UI 框架,由于声明式开发理念在当时还过于 ...

  9. Jetpack Compose 从入门到入门(四)

    本篇开始介绍Jetpack Compose 中常用的组件.有一部分之前的文章中也出现过,今天详细说明一下. 1. Text 日常最常用的应该就是显示文字,所以有必要说一下Text控件.首先源码如下: ...

  10. Jetpack Compose入门

    简介 Jetpack Compose是用于构建原生Android界面的新工具包.它是一种声明式的UI布局,其官方声称可简化并加快Android上的界面开发,使用更少的代码.强大的工具和直观的Kotli ...

最新文章

  1. 用原生js的postMessage实现iframe传值,也可以用于跨域嵌套iframe传值
  2. android.xml设置全屏,Android全屏设置的方法总结
  3. 目标检测中背景建模方法
  4. 通过RocketMQ的java客户端api进行测试
  5. rest_framework08:分页器/根据ip进行频率限制
  6. Docker 网络命名空间
  7. python生成指定长度的列表_如何在python中创建固定大小列表?
  8. mysql安装数据自定义_mysql数据库自定义怎么安装
  9. 【C语言】又是排序(指针专题)
  10. mybatis之xml中日期时间段查询的sql语句
  11. 【论文写作】SSM房屋租赁系统如何写设计总结
  12. 数字电视DVB-T/T2/C/S/S2,ATSC,ISDB-T参数设置
  13. 2016年求职找工作千万小心这些求职陷阱
  14. 标梵分析SEM竞价托管的选择方式
  15. iOS App各种路径
  16. 【经典论文解读】YOLO 目标检测
  17. 正则类:判断一个字符串是否全是数字
  18. coursera课程,coursera课程下载学习
  19. http状态码大全,从100-505状态码详情
  20. 【dsPIC33E】Bootloader(三)Bootloader下位机

热门文章

  1. RAID技术分类介绍
  2. C#学习笔记-winform和wpf 事件绑定理解
  3. Android实现简单的欢迎界面
  4. mysql 默认排序是什么意思,MySQL 默认排序是什么
  5. 蓝桥杯第十届c语言试题答案,[蓝桥杯][2019年第十届真题]空间跳跃 - C语言网
  6. RNN预测股票开盘价(TensorFlow,tensorboard可视化)
  7. bitmap 设置图片尺寸,避免 内存溢出 OutOfMemoryError的优化方法
  8. 论坛介绍 | COSCon'22 开源操作系统(O)
  9. unicloud进阶uni-id入门(一)---uni-id能做什么?
  10. java判断是否英文_java如何判断字符串是否是英文