JetPack Compose之Modifier修饰符

前言

在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的时候才会事半功倍。有余力的读者也可以看下Modifier的实现原理,然后分享出来大家一起学习。

猜你喜欢

转载自blog.csdn.net/zxj2589/article/details/129813905