Android Compose Modifier修饰符

在Compose中,Modifier承担了绝大部分的修饰作用,通过它可以为控件设置width、height、size、padding、background等等。

一 链式调用

在Compose中每个控件都有Modifier属性,Modifier包含了许多属性设置,它可以通过链式调用的方式对控件进行修饰。

Kotlin 复制代码
@Composable
private fun Greeting(name: String) {
    Column(
        modifier = Modifier
            .padding(24.dp)
            .fillMaxWidth()
    ) {
        Text(text = "Hello,")
        Text(text = name)
    }
}

二 链式调用顺序

修饰符链式调用的顺序是会影响最终的显示效果的。通过如下例子可以了解:

Kotlin 复制代码
@Preview
@Composable
fun CallOrderTest1(){
    val padding = 16.dp
    Column(
        Modifier
            .background(Color.Blue)
            .padding(padding)
            .fillMaxWidth()

    ) {
        Text(text = "Hello,")
        Text(text = "Android")
    }
}
Kotlin 复制代码
@Preview
@Composable
fun CallOrderTest1(){
    val padding = 16.dp
    Column(
        Modifier
            .padding(padding)
            .background(Color.Blue)
            .fillMaxWidth()

    ) {
        Text(text = "Hello,")
        Text(text = "Android")
    }
}

上面两段代码仅仅对

复制代码
.padding(padding)
.background(Color.Blue)

的顺序进行了调换,展现的效果却是不一样的,由此可见,调用顺序对最终效果是有影响的。

三 Compose中的修饰符作用域安全

单看这个标题,你可能一脸懵,接下来,请听我解释

(一)传统XML中存在的问题

让我们继续从传统XML的视角来了解,下面是两个传统XML布局

XML 复制代码
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="@color/teal_200"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 线性布局里,layout_gravity 基本没用 -->
    <!-- 但 XML 允许你写,不会报错 -->
    <View
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:background="@color/purple_200"
        android:layout_gravity="bottom"/>  <!-- 无效属性,XML不提示 -->

</LinearLayout>

实际效果:View的位置依旧在左上角,并没有 设置layout_gravity="bottom"预期的在下方的效果,可见layout_gravity属性的设置并没有生效,但上述xml代码编译和运行时期都没有报错提示

XML 复制代码
<!-- 父布局:CoordinatorLayout黄色背景 -->
<androidx.coordinatorlayout.widget.CoordinatorLayout 
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:background="#FFEB3B"
    xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- 子 View1红色背景:试图用 weight=1 平分宽度 -->
    <View
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="#80CF0000"/>

    <!-- 子 View2蓝色背景:试图用 weight=1 平分宽度 -->
    <View
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:background="#800000CF"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

实际效果:并没有达到原本想要的红蓝平分的效果,而是红蓝都不展示,说明layout_weight属性根本没有生效,但这份xml代码在编译和运行时都没有报错

通过上面两个例子我们可以知道,在传统的XML布局中,很多属性都是没有严格限制使用区域的,也就是可以在不属于它修饰的区域编写编译运行成功,这样一来,我们就很容易写错属性,但

  • 写错不会报错,只会布局错乱
  • 必须靠反复调试才能发现问题

而Compose就很好的解决了这个问题。

(二)Compose的解决

Compose利用 Kotlin 隐式 Receiver + @LayoutScopeMarker 注解,在编译期限制 API 只能在对应父组件内部使用,杜绝 View 体系无效 LayoutParams 的坑

例如:

  • .weight()只能在Row/Column{}里调用

正确调用:在Column{}里调用

错误调用:在非在Box{}里调用

  • .matchParentSize()只能在Box{}里调用

正确调用:在Box{}里调用

错误调用:在Column{}里调用

不在对应 Scope调用直接标红编译失败 ,就是标题所讲的修饰符作用域安全

四 抽取和重用修饰符

上面我们讲了,我们几乎可以为每一个组件设置Modifier修饰符,而Modifier修饰符本身是一个对象,故而我们可以在合适的位置对Modifier对象进行抽取和重用。

这样做的好处是:

  • 改一处,全局生效
  • 代码更简洁
  • 比 XML style 更灵活
  • 对于一些需要频繁重绘的控件(如动画或计数器),抽取可以提高性能,若不抽取,每重绘一次就需要重新创建一次对象。

(一)抽取公共样式(圆角、背景、内边距)

Kotlin 复制代码
// 1. 定义公共复用的 Modifier(顶层、伴生对象里都可以)
val CommonCardModifier = Modifier
    .fillMaxWidth()
    .padding(16.dp)
    .background(Color.White, RoundedCornerShape(12.dp))
    .padding(16.dp)

// 2. 在组件中直接复用
@Preview
@Composable
fun UserCard() {
    Box(modifier = CommonCardModifier) {
        Text("用户卡片")
    }
}

@Preview
@Composable
fun NewsCard() {
    Box(modifier = CommonCardModifier) {
        Text("新闻卡片")
    }
}

(二)带参数的可配置复用 Modifier

Kotlin 复制代码
// 带参数的公共 Modifier 扩展函数
fun Modifier.itemStyle(
    bgColor: Color = Color.White,
    cornerSize: Dp = 8.dp
) = this.padding(8.dp)
    .background(bgColor, RoundedCornerShape(cornerSize))
    .padding(12.dp)

// 使用
@Preview
@Composable
fun ItemList() {
    Column{
        Text(
            text = "条目1",
            modifier = Modifier.itemStyle(Color.Gray) // 传参定制
        )
        Text(
            text = "条目2",
            modifier = Modifier.itemStyle() // 使用默认值
        )
    }
}

(三)组合多个 Modifier(超级复用)

Kotlin 复制代码
val BaseModifier = Modifier.fillMaxWidth()

val FormModifier = BaseModifier
    .padding(16.dp)
    .background(Color.White)

val CardModifier = FormModifier
    .clip(RoundedCornerShape(16.dp))

(四)抽取频繁重绘组件中的Modifier

Kotlin 复制代码
// 抽取到 Composable 函数外部(顶层/伴生对象)
// 全局只创建 一次 !!!
val CounterTextModifier = Modifier
    .padding(16.dp)
    .background(Color.Cyan)
    .clickable { }

@Composable
fun Counter() {
    var count by remember { mutableStateOf(0) }

    Column {
        Text(
            text = "计数:$count",
            // 直接复用已有的对象,不创建新对象
            modifier = CounterTextModifier
        )
    }
}
相关推荐
黄林晴1 小时前
Android17新规:内存超限直接杀App,没有崩溃日志怎么排查?
android
Yeyu1 小时前
Binder 阻塞检测:跨进程通信的性能陷阱与监控方案
android·性能优化
●VON2 小时前
鸿蒙Flutter实战:日期选择器与截止日期高亮提醒
android·flutter·华为·harmonyos·鸿蒙
流星白龙2 小时前
【MySQL高阶】20.InnoDB 磁盘文件
android·mysql·adb
●VON2 小时前
鸿蒙Flutter实战:Material 3种子色亮暗双主题系统
android·flutter·harmonyos
灰鲸广告联盟2 小时前
新老用户广告价值不同?差异化策略如何实现收益最大化
android·开发语言·flutter·ios
朱涛的自习室3 小时前
逃离“古法测试”:AI 测试的“三大定律”
android·前端·人工智能
小书房3 小时前
Android UI为什么由XML转向Compose
xml·ui·compose·声明式ui
QING6183 小时前
Android面试 —— 八股文(一)
android·面试·android jetpack