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
        )
    }
}
相关推荐
狂奔solar18 小时前
ARMA 模型:从白噪声到万能逼近器
android
渣渣灰飞18 小时前
MySQL 系统学习 第五阶段:企业级 MySQL 实战开发 第二章:RBAC 权限系统设计
android·学习·mysql
qq_4228286218 小时前
android 图形学之图层数据送显及合成图层(七)
android
MiyamuraMiyako20 小时前
Compose:从自动滚动到双锚点 LazyLayout
android·android jetpack
用户634214199350721 小时前
base_logger_core --- 跨平台 C++ 日志核心库
android
苦瓜花21 小时前
【Kotlin】初入门
android·开发语言·kotlin
dora1 天前
使用 Cling 实现 DLNA 投屏:从局域网发现到媒体播放控制
android·android things·tvos
apihz1 天前
全球域名 WHOIS 信息实时查询免费 API 接口教程,支持1000+后缀
android·网络·网络协议·tcp/ip·apache·域名·whois
非典型Android程序员1 天前
Binder机制学习总结
android
AI2中文网1 天前
App Inventor 2 音频播放器 vs 音效 vs 视频播放器:三个媒体组件,选错了App直接卡死
android·音视频·媒体