美文网首页
JetPack Compose之Modifier修饰符

JetPack Compose之Modifier修饰符

作者: 艾瑞败类 | 来源:发表于2023-04-03 21:42 被阅读0次

    作者:时光剑客

    前言

    在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的时候才会事半功倍。

    相关文章

      网友评论

          本文标题:JetPack Compose之Modifier修饰符

          本文链接:https://www.haomeiwen.com/subject/lgtcddtx.html