前面我们介绍了Jetpack Compose的环境的配置,以及Column , Row ,Box 的用法,这篇文章开始介绍Modifier。 Modifier 是Composable的修饰符,他是一个标准的Kotlin对象。
一:Modifie的作用
以前,我们在布局中去设置一个控件的大小,间距,点击事件,宽高,背景等属性值。而在Compose中我们是通过Modifie去设置,Modifie相当于一个控件的属性配置的工具类。修饰符大概有如下几种作用
- 第一:可以去更新可组合项的大小,布局,行为和外观。
- 第二:添加互动。例如点击,滚动,可拖拽,缩放等。
- 处理用户输入
- 添加信息,如无障碍标签
例如:
@Composable
fun ArtistCard(
artist: Artist,
onClick: () -> Unit
) {
Column(
modifier = Modifier
.clickable(onClick = onClick)
.padding(16.dp)
.fillMaxWidth()
) {
Row(verticalAlignment = Alignment.CenterVertically) { /*...*/ }
Spacer(Modifier.size(20.dp))
Card(elevation = 4.dp) { /*...*/ }
}
}
上面的代码中,我们通过Modifier类调用的方式,去调用不同的修饰符函数。
- clickable 使可组合项响应用户输入,并显示涟漪。
- padding 在元素周围留出空间。
- fillMaxWidth 使可组合项填充其父项为它提供的最大宽度。
- size() 指定元素的首选宽度和高度
注意:修饰符的函数调用顺序非常重要。由于每个函数都会对上一个函数返回的 Modifier 进行更改,因此顺序会影响最终结果。比如上面是对Column整体控件都可以点击,但如果我们把,padding跟clickable的修饰符的顺序换一下。那么Column的点击范围就需要扣除padding的部分。
@Composable
fun ArtistCard(/*...*/) {
val padding = 16.dp
Column(
modifier = Modifier
.clickable(onClick = onClick)
.padding(padding)
.fillMaxWidth()
) {
// rest of the implementation
}
}
二:常用的Modifier的函数方法介绍
Modifier涉及到的方法很多,我们可以从官网的地址去查看Modifier方法,下面我们会进可能的去涉及解释到常用方法的使用。
- background 设置背景
@Composable fun PaddedComposable() { Box(modifier = Modifier.size(300.dp,300.dp).background(Color.Blue)){} }
- padding 设置边距(可以设置全部,也可以设置左start,上top,右end,底bottom分别设置,也可以设置horizontal横向,竖向vertical)
@Composable fun PaddedComposable() { Text("Hello World", modifier = Modifier.padding(20.dp)) } @Preview() @Composable fun paddingTest(){ Column(modifier = Modifier.size(200.dp).background(Color.Gray).padding(20.dp)) { Box(modifier = Modifier.size(30.dp).background(Color.Red).padding(top = 4.dp)) {} Box(modifier = Modifier.size(30.dp).padding(start = 4.dp,top = 4.dp).background(Color.Green)) {} Box(modifier = Modifier.size(30.dp).padding(vertical = 4.dp).background(Color.Red)) {} Box(modifier = Modifier.size(30.dp).padding(horizontal = 4.dp).background(Color.Green)) {} } }
- size 指定元素的宽高
@Preview() @Composable fun modiferTest(){ Box(modifier = Modifier.size(300.dp,300.dp){} }
- requiredSize:请注意,如果指定的尺寸不符合来自布局父项的约束条件,则可能不会采用该尺寸。如果您希望可组合项的尺寸固定不变,而不考虑传入的约束条件,请使用 requiredSize 修饰符
比如外面的布局大小是90,150,而里面需要100dp的宽度超出了父项的约束,这时候希望子项有作用,那么可以使用requiredSize@Composable fun FixedSizeComposable() { Box(Modifier.size(90.dp, 150.dp).background(Color.Green)) { Box(Modifier.requiredSize(100.dp, 100.dp).background(Color.Red)) } }
- fillMaxHeight 填充父项为它提供的最大的高度
- fillMaxWidth 填充父项为它提供的最大的宽度
- fillMaxSize 填充父项的宽高
- width 宽度
- height 高度
@Preview() @Composable fun modiferTest(){ Box(modifier = Modifier.size(300.dp,300.dp).background(Color.Blue)){ Box(modifier = Modifier.fillMaxHeight().fillMaxWidth().background(Color.Green)) Box(modifier = Modifier.fillMaxWidth().height(50.dp).background(Color.Red)) Box(modifier = Modifier.width(50.dp).fillMaxHeight().background(Color.Black)) Box(modifier = Modifier.fillMaxSize().background(Color.Gray)) } }
- wrapContentSize 根据子级元素的宽高来确定自身的宽度和高度,如果自身设置了最小宽高的话则会被忽略。当unbounded参数为true的时候,自身设置了最大宽度的话也会被忽略
- wrapContentWidth 根据子级元素的宽度来确定自身的宽度,如果自身设置了最小宽度的话则会被忽略。当unbounded参数为true的时候,自身设置了最大宽度的话也会被忽略
- wrapContentHeight 根据子级元素的高度来确定自身的高度,如果自身设置了最小高度的话则会被忽略。当unbounded参数为true的时候,自身设置了最大高度的话也会被忽略
@Preview() @Composable fun modiferTest(){ Column(modifier = Modifier.wrapContentSize().background(Color.Blue).padding(10.dp){ Box(modifier = Modifier.size(30.dp, 30.dp).background(Color.Red)) {} Box(modifier = Modifier.padding(top = 4.dp).wrapContentWidth().height(60.dp).background(Color.Green).padding(60.dp)) Box(modifier = Modifier.padding(top = 4.dp).width(60.dp).wrapContentHeight().background(Color.Gray).padding(30.dp)){} } }
- preferredWidth 设置初始宽度
- preferredHeight 设置初始高度
- preferedSize 设置初始的宽高
preferedXXX 等是用来设置初始的size,这个值可能被其他约束覆盖。
@Preview() @Composable fun preferredTest(){ Column(modifier = Modifier .size(150.dp) .background(Color.Red)){ Box(modifier = Modifier.preferredSize(40.dp).background(Color.Green)) Box(modifier = Modifier.padding(top = 4.dp).preferredSize(40.dp,20.dp).background(Color.Green)) Box(modifier = Modifier.padding(top = 4.dp).preferredWidth(40.dp).height(20.dp).background(Color.Green)) Box(modifier = Modifier.padding(top = 4.dp).width(20.dp).preferredHeight(40.dp).background(Color.Green)) } }
- widthIn(最小宽度,最大宽度) 设置自身的最小,最大宽度
- heightIn(最小高度,最大高度) 设置自身的最小,最大高度
- sizeIn(最小宽度,最小高度,最大宽度,最大高度) 设置自身的最小,最大宽高
@Preview() @Composable fun preferredTest(){ Column(modifier = Modifier .size(300.dp) .background(Color.Red)){ Text(text = "测试测试测试", modifier = Modifier.sizeIn(40.dp,40.dp,100.dp,100.dp)) Text(text = "测试测试,测试测试,测试测试", modifier = Modifier.sizeIn(40.dp,40.dp,100.dp,100.dp)) Text(text = "测试", modifier = Modifier.widthIn(20.dp,100.dp)) Text(text = "测试,测试,测试", modifier = Modifier.widthIn(20.dp,100.dp)) Text(text = "测试", modifier = Modifier.heightIn(20.dp,100.dp)) Text(text = "测试,测试,测试,测试,测试,测试,测试,测试,测试,测试,测试", modifier = Modifier.heightIn(20.dp,100.dp)) } }
- preferredWidthIn(最小宽度,最大宽度) 设置初始最小和最大宽度
- preferredHeightIn(最小高度,最大高度) 设置初始最小和最大高度
- preferedSizeIn(最小宽度,最小高度,最大宽度,最大高度) 设置初始最小和最大宽高
@Preview() @Composable fun preferredTest(){ Column(modifier = Modifier .size(300.dp) .background(Color.Red)){ Text(text = "测试测试", modifier = Modifier.preferredSizeIn(40.dp,40.dp,100.dp,100.dp)) Text(text = "测试测试,测试测试,测试测试", modifier = Modifier.preferredSizeIn(40.dp,40.dp,100.dp,100.dp)) Text(text = "测试", modifier = Modifier.preferredWidthIn(20.dp,100.dp)) Text(text = "测试,测试,测试", modifier = Modifier.preferredWidthIn(20.dp,100.dp)) Text(text = "测试", modifier = Modifier.preferredHeightIn(20.dp,100.dp)) Text(text = "测试,测试,测试,测试,测试,测试,测试,测试,测试,测试,测试,测试", modifier = Modifier.preferredHeightIn(20.dp,100.dp)) } }
- matchParentSize 如果您希望将子布局的尺寸设置为与父 Box 相同,但不影响 Box 的尺寸,请使用 matchParentSize 修饰符。请注意,matchParentSize 仅在 Box 作用域内可用,这意味着它仅适用于 Box 可组合项的直接子项
@Composable fun MatchParentSizeComposable() { Box { Spacer(Modifier.matchParentSize().background(Color.Green)) Text("Hello World") } }
- paddingFromBaseline(top,bottom) paddingFromBaseline如果top值,表示基准线距离父项的顶部是多少,bottom值是表示基准线距离父项的底部是多少
@Preview() @Composable fun paddingFromBaseLineTest(){ Box(Modifier.background(Color.Yellow)) { Text(text = "Hi there",Modifier.paddingFromBaseline(top = 40.dp,bottom = 20.dp)) } }
- offset(x,y) 设置偏移量. x是相对于x轴上的偏移,y是相对于y轴上的偏移。
- absoluteOffset(x,y)设置偏移量
@Preview()
@Composable
fun offsetTest(){
Column(Modifier.background(Color.Yellow).size(width = 150.dp, height = 70.dp)) {
Text(text = "Hi here",Modifier.offset(x=10.dp,y = 20.dp))
Text(text = "Hi here two",Modifier.absoluteOffset(x=10.dp,y = 20.dp))
}
}
offset 修饰符根据布局方向水平应用。在从左到右的上下文中,正 offset 会将元素向右移,而在从右到左的上下文中,它会将元素向左移,如果您需要设置偏移量,而不考虑布局方向,请参阅 absoluteOffset 修饰符,其中,正偏移值始终将元素向右移
- align 设置子元素在垂直方向如何对齐。(只能在Row中使用)
@Preview() @Composable fun alignTest() { Row(Modifier.width(210.dp).height(100.dp)) { Box(Modifier.align(alignment = Alignment.CenterVertically).width(20.dp).height(50.dp).background(Color.Blue)) } }
- weight 按占比去设置(只能在Row和Column中使用),类似于LinearLayout的layout_weight
比如内部两个 Box 可组合项的 Row 为例。第一个框的 weight 是第二个框的两倍,由于 Row 的宽度为 210.dp,因此第一个 Box 的宽度为 140.dp,第二个的宽度为 70.dp
@Preview() @Composable fun FlexibleComposable() { Row(Modifier.width(210.dp)) { Box(Modifier.weight(2f).height(50.dp).background(Color.Blue)) Box(Modifier.weight(1f).height(50.dp).background(Color.Red)) } }
- aspectRatio 按照宽高比例进行设置,比如宽设置了80dp,ratio是2f,那么高自动会为40
@Preview()
@Composable
fun modifierAspectRadioTest(){
Box(modifier = Modifier.width(80.dp).aspectRatio(ratio = 2f, matchHeightConstraintsFirst = false).background(Color.Yellow))
}
- border 设置边框。比如在column里面设置个圆角的矩形,border的第一个参数是边框的宽度,第二个是颜色,第三个是shape。
@Preview()
@Composable
fun borderTest(){
Column(modifier = Modifier.size(200.dp),horizontalAlignment= Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center) {
Box(modifier = Modifier.size(30.dp).border(width = 1.dp,color = Color.Blue,shape = RoundedCornerShape(4.dp)).background(Color.Yellow)) {}
}
}
- clickable 设置事件。可以设置点击,长按,双击等事件。举个简单的例子。Column点击的时候提示toast
@Composable
fun tabItem(){
val context = ContextAmbient.current
Column(modifier = Modifier.clickable(
enabled = true,
onClickLabel = "点击的lable",
onClick = {
Toast.makeText(context, "点击了", Toast.LENGTH_SHORT).show()
},
onLongClickLabel = "长按的lable",
onLongClick = {
Toast.makeText(context, "长按了", Toast.LENGTH_SHORT).show()
},
onDoubleClick = {
Toast.makeText(context, "双击了", Toast.LENGTH_SHORT).show()
}
)){
Icon(vectorResource(id = R.drawable.ic_launcher_background))
Text(text = "哈哈哈")
}
}
- alpha 设置不透明度,范围是从0-1。
- rotate 设置视频围绕其中心点旋转的角度
- scale 设置视图的缩放比例
- clip(shape) 设置裁剪。根据传入的shape,可以裁剪成对应的图行。比如头像裁剪成圆角的例子
- shadow 绘制阴影效果,第一个参数是阴影大佬,第二个是shape
@Preview()
@Composable
fun clipTest(){
val image = imageResource(id = R.drawable.icon_head)
val imageModifier = Modifier
.size(40.dp)
.alpha(0.5f) //设置透明度
.scale(0.5f) //设置缩放
.rotate(90f) //设置旋转角度
.clip(RoundedCornerShape(8.dp))
Column{
Image(bitmap = image, modifier = imageModifier, contentScale = ContentScale.Crop)
Box(modifier = Modifier.size(50.dp).shadow(elevation=2.dp,shape = CircleShape).clip(CircleShape).background(Color.Yellow))
}
}
- horizontalScroll 设置水平滚动
// 首先我们定义一个Student类 class Student(var name:String=""){} @Composable fun studentRowItem(student:Student){ Text(text = student.name,modifier = Modifier .fillMaxHeight() .width(60.dp),fontSize = 14.sp,textAlign = TextAlign.Center) } @Preview @Composable fun rowScrollTest(){ val list = ArrayList<Student>() for(i in 0..20){ list.add(Student(i.toString())) } Row(modifier = Modifier .fillMaxWidth() .height(100.dp) .horizontalScroll(rememberScrollState()) ) { list.forEach { studentRowItem(student = it) } } }
- verticalScroll 设置竖直滚动
class Student(var name:String=""){} @Preview @Composable fun columnScrollTest(){ val list = ArrayList<Student>() for(i in 0..20){ list.add(Student(i.toString())) } studentList(students = list) } @Composable fun studentList(students:List<Student>){ Column(modifier = Modifier .fillMaxWidth() .height(100.dp) .verticalScroll(rememberScrollState()) ) { students.forEach { studentItem(student = it) } } } @Composable fun studentItem(student:Student){ Text(text = student.name,modifier = Modifier.fillMaxWidth().height(30.dp),fontSize = 14.sp,textAlign = TextAlign.Center) }