作者:时光剑客
前言
在Compose中,每一个组件都是带有@Compose注解的函数,被称为Composable。Compose已经预置了很多的Compose UI组件,这些组件都是基于Material Design规范设计的,例如Button,TextField,TopAPPBar等。在布局上,Compose提供了Column,Row,Box三种布局组件,这些布局组件的使用类似于传统视图开发的LinearLayout(Vertical),LinearLayout(Horizontal),FrameLayout。而这些UI组件的使用都离不开一个重要的概念,那就是Modifier修饰符,它决定了UI组件的样式,比如背景,填充,布局等。
Modifier常用修饰符
Modifier.size
size修饰符用于设置被修饰组件的大小,使用如下所示:
@Composable
fun ShowCircleImg() {
Row {
Image(
painterResource(id = R.drawable.portrait),
contentDescription = null,
modifier = Modifier
.size(60.dp)
.clip(
CircleShape
)
)
Spacer(modifier = Modifier.width(10.dp))
Image(
painterResource(
id = R.drawable.portrait
),
contentDescription = null,
modifier = Modifier
.size(100.dp)
.clip(CircleShape)
)
Spacer(modifier = Modifier.width(10.dp))
// 使用size的重载方法单独设置组件的宽度和高度
Image(
painterResource(
id = R.drawable.portrait
),
contentDescription = null,
modifier = Modifier.size(
width = 200.dp,
height = 500.dp
)
)
}
}
在上面的代码中我们展示两个大小不一样的圆形图片,运行结果如下:
size提供重载方法,支持单独设置组件的宽度与高度
Modifier.background
background 修饰符用来为组件添加背景色,背景色支持设置Color的纯色背景,也可以使用brush设置渐变色背景。这里的Brush是Compose提供的用于创建线性变色的工具,如下面的代码所示:
@Composable
fun ShowModifierBG() {
Row {
Box(
modifier = Modifier
.size(200.dp)
.background(color = Color.Red)
) {
Text(text = "纯色", Modifier.align(Alignment.Center))
}
Spacer(modifier = Modifier.width(10.dp))
Box(
modifier = Modifier
.size(200.dp)
.background(brush = verticalGradient)
) {
Text(text = "渐变色", Modifier.align(Alignment.Center))
}
}
}
运行结果:
注意:传统视图中View的background属性可以用来设置图片格式的背景,Compose的background修饰符只能设置颜色背景,图片背景需要使用Box布局配合Image组件实现。
Modifier.fillMaxSize
当我们想要组件的宽度或者是高度填满父容器空间时,我们可以使用Compose提供的fillMaxXXX系列方法 填满整个父容器空间的代码如下:
Box(modifier = Modifier
.fillMaxSize()
.background(Color.Red))
高度填满父容器空间
Box(modifier = Modifier
.fillMaxHeight()
.width(60.dp)
.background(Color.Green))
宽度填满父容器空间
Box(modifier = Modifier
.fillMaxWidth()
.height(60.dp)
.background(Color.Yellow))
Modifier.border和Modifier.padding
border修饰符用来为被修饰的组件添加边框,这个边框可以指定颜色,粗细,以及通过Shape指定形状,比如圆角矩形等。padding用来为被修饰的组件增加间隙。可以再border前后各插入一个padding,区分对外和对内的间距,代码如下:
@Composable
fun PaddingAndBorder() {
Box(
modifier = Modifier
.padding(8.dp) // 外间隙
.border(2.dp, Color.Red, shape = RoundedCornerShape(2.dp))
.padding(8.dp) // 内间隙
) {
Spacer(
modifier = Modifier
.size(width = 200.dp, height = 40.dp)
.background(Color.Red)
)
}
}
运行的效果如下: 在这里插入图片描述
注意:在传统布局里面又Margin和padding之分,Compose中只有padding一种修饰符,根据在调用链中的位置不同而发挥不同的作用。
## Modifier.offset offset用来移动被修饰组件的位置,我们在使用的时候只分别传入水平方向和垂直方向的偏移量即可。代码如下:
@Composable
fun ModifierOffset() {
Box(modifier =
Modifier
.size(300.dp)
.offset(x = 30.dp, y = 75.dp)
.background(Color.Red))
}
运行结果如下:
如上图所示,我们修改offset的值,图中红色方块的位置就会发生改变,读者可以运行到手机上后手动修改这个值,看手机上的界面变化。
注意:Modifier的调用顺序会影响最终UI呈现的效果,这里应该先使用offset修饰符偏移,再使用background修饰符绘制背景色
我们也可以使用offset的重载方法修改组件的位置,代码如下:
@Composable
fun ModifierCustomOffset() {
Box(modifier =
Modifier
.size(300.dp)
.offset { IntOffset(200.dp.roundToPx(), 150.dp.roundToPx()) }
.background(Color.Red))
}
Modifier作用域限定修饰符
Compose 利用Kotlin的语法特性,让某些Modifier修饰符只能再特定的作用域中使用,这样有利于类型安全的调用他们,其实所谓的作用域,就是Kotlin中一个带有Receiver的代码块,例如Box组件参数的content就是一个Receiver类型为BoxScope的代码块,因此其子组件都处于BoxScope作用域中
@Composable
inline fun Box(
modifier: Modifier = Modifier,
contentAlignment: Alignment = Alignment.TopStart,
propagateMinConstraints: Boolean = false,
content: @Composable BoxScope.() -> Unit
) {
}
Box{
// 该代码块Reciever即为BoxScope
}
不过我们需要注意的是,Reciever类型默认是可以跨层级访问的,例如下面的代码:
class AScope {
fun visitA() {}
}
class BScope {
fun visitB() {}
}
fun funA(scope: AScope.() -> Unit) {
scope(AScope())
}
fun funB(scope: BScope.() -> Unit) {
scope(BScope())
}
fun main() {
funA {
funB {
visitA()
}
}
}
由于funB{...} 处于funA{...}内部,所以可以在funB{...}中访问属于funA{...}的方法visitA(),而在Compose的DSL中一般只需要调用当前作用域的方法,跨级访问会加大出错的概率,所以Compose提供了@LayoutScopemarker注解来规避Receiver的跨级访问。
@LayoutScopeMarker
@Immutable
interface BoxScope
我们常用的组件Receiveier作用域均已使用@LayoutScopemarker注解进行声明,添加了该组件的Receiver,我们在其作用域中默认只能调用作用域提供的方法。像跨级调用外层作用域的方法时,必须通过显示知名Receiver的类型。
作用域限定修饰符的好处在于类型安全,这在传统视图中是难以保证的,例如我们可以在布局的xml文件中为LinearLayout的子组件设置android:toRightOf属性,这设置对父布局没有任何意义,可能还会出错,但是我们很难做到根据不同夫类型安全地调用修饰符,而Compose的作用域限定符实现了Modifier的安全调用,我们只能在特定作用域中调用修饰符
matchParentSize
matchParentSize是只能在BoxScope中使用的作用域限定修饰符,当使用matchParentSize设置尺寸时,可以保证当前组件的尺寸与父组件相同。而父组件默认是wrapContent,这个属性会根据同级组件的尺寸来确定自身的尺寸,代码如下: 我们先定义一个简单的卡片子组件,用于展示效果:
@Composable
fun PhotoCard(modifier: Modifier = Modifier){
Row(
modifier = modifier
.clip(RoundedCornerShape(4.dp))
.background(MaterialTheme.colors.surface)
.clickable { }.padding(16.dp)
) {
Surface(
modifier = modifier.size(50.dp),
shape = CircleShape,
color = MaterialTheme.colors.onSurface.copy(alpha = 0.2f)
) {
Image(
painter = painterResource(id = R.drawable.portrait),
contentDescription = ""
)
}
Column(
modifier = modifier.padding(start = 8.dp).align(Alignment.CenterVertically)
) {
Text(text="Zhongxj", fontWeight = FontWeight.Bold)
Text(text = "3 分钟前", style = MaterialTheme.typography.body2)
}
}
}
然后使用matchParentSize修饰与卡片同级的BOX组件,为了演示,我特地在最外层组件上加了一个灰色背景和padding,不然直接就看不到效果了。
@Composable
fun MatchParentSizeDemo() {
Box(
modifier = Modifier
.background(Color.Gray)
.padding(10.dp)
) {
Box(
modifier = Modifier
.matchParentSize()
.background(Color.Red)
)
PhotoCard()
}
}
代码运行结果如下:
这里的fillParentSize如果换成fillMaxSize,那么该组件的尺寸会被设置为父组件所允许的最大尺寸,这样会导致背景铺满整个屏幕
## weight 在RowScope和ColumnScope中,可以使用专属的weight来设置尺寸,与size修饰符不同的是,weight修饰符允许组件通过百分比设置尺寸,也就是允许组件可以自适应适配各种屏幕尺寸的终端设备。 例如,我们希望绿色,红色,蓝色方块共享一整块column空间,其中每种颜色方块高度各占比1/3,代码如下:
@Composable
fun WeightModifierDemo() {
Column(
modifier = Modifier
.fillMaxWidth()
.height(200.dp)
) {
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.background(Color.Green)
) {
Text(text = "White", modifier = Modifier.align(Alignment.Center))
}
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.background(Color.Red)
) {
Text(text = "Red", modifier = Modifier.align(Alignment.Center))
}
Box(
modifier = Modifier
.weight(1f)
.fillMaxWidth()
.background(Color.Blue)
) {
Text(text = "Blue", modifier = Modifier.align(Alignment.Center))
}
}
运行结果:
总结
本文关于Modifier修饰符就介绍到这里,是不是感觉Compose UI写起来特别爽,Modifier修饰符是特别重要的一个概念,建议读者应该在自己的电脑上手动敲下这些小例子,熟悉下Modifier的使用,这样在后面开发ComposeUI的时候才会事半功倍。
网友评论