样式基础知识

您可以通过以下三种方式在整个应用中采用样式:

  1. 直接用于公开 Style 参数的现有组件。
  2. 对不接受 Style 参数的布局可组合项应用 Modifier.styleable 样式。
  3. 在您自己的自定义设计系统中,使用 Modifier.styleable{} 并为自己的组件公开样式参数。

样式中的可用属性

样式支持许多与修饰符相同的属性;不过,并非所有修饰符都可以通过样式进行复制。您仍然需要修饰符来实现某些行为,例如互动、自定义绘制或堆叠属性。

分组 属性 由子级继承
布局和尺寸调整
内边距 contentPadding(内部)和 externalPadding(外部)。提供定向、水平、垂直和全方位变体。
尺寸 fillWidth/Height/Size()widthheightsize(支持 DpDpSizeFloat 分数)。
Positioning left/top/right/bottom 偏移量。
视觉外观
充满电 backgroundforeground(支持 ColorBrush)。
边框 borderWidthborderColorborderBrush
形状 shape 否 - 但会与其他属性结合使用。clipborder 使用此定义的形状。
阴影 dropShadowinnerShadow
变换
图形层空间移动 translationXtranslationYscaleX/YrotationX/Y/Z
控制 alphazIndex(堆叠顺序)和 transformOrigin(透视点)
排版
样式 textStylefontSizefontWeightfontStylefontFamily
着色 contentColorcontentBrush。这也用于图标样式设置。
段落 lineHeightletterSpacingtextAligntextDirectionlineBreakhyphens
装饰 textDecorationtextIndentbaselineShift

使用样式参数直接在组件上使用样式

公开 Style 参数的组件允许您设置其样式:

BaseButton(
    onClick = { },
    style = { }
) {
    BaseText("Click me")
}

在样式 lambda 中,您可以设置各种属性,例如 externalPaddingbackground

BaseButton(
    onClick = { },
    style = { background(Color.Blue) }
) {
    BaseText("Click me")
}

如需查看受支持属性的完整列表,请参阅样式中的可用属性

使用修饰符为没有现有参数的组件应用样式

对于缺少内置样式参数的组件,您仍然可以使用 styleable 修饰符应用样式。在开发自己的自定义组件时,此方法也很有用。

Row(
    modifier = Modifier.styleable { }
) {
    BaseText("Content")
}

style 参数类似,您可以在 lambda 内添加 backgroundpadding 等属性。

Row(
    modifier = Modifier.styleable {
        background(Color.Blue)
    }
) {
    BaseText("Content")
}

多个链接的 Modifier.styleable 修饰符会与所应用的可组合项上的非继承属性相加,其行为类似于定义相同属性的多个修饰符。对于继承的属性,这些属性会被覆盖,并且链中的最后一个 styleable 修饰符会设置相应的值。

使用 Modifier.styleable 时,您可能还需要创建并提供 StyleState,以便与修饰符搭配使用来应用基于状态的样式。如需了解详情,请参阅使用样式设置状态和动画

定义独立样式

您可以定义一个独立的样式以供重复使用:

val style = Style { background(Color.Blue) }

然后,您可以将定义的样式传递到可组合项的样式参数中,也可以通过 Modifier.styleable 传递。使用 Modifier.styleable 时,您还需要创建一个 StyleState 对象。StyleState使用样式设置状态和动画文档中进行了详细介绍。

以下示例展示了如何通过组件的内置参数或通过 Modifier.styleable 直接应用样式:

val style = Style { background(Color.Blue) }

// built in parameter
BaseButton(onClick = { }, style = style) {
    BaseText("Button")
}

// modifier styleable
val styleState = remember { MutableStyleState(null) }
Column(
    Modifier.styleable(styleState, style)
) {
    BaseText("Column content")
}

您还可以将该样式传递给多个组件:

val style = Style { background(Color.Blue) }

// built in parameter
BaseButton(onClick = { }, style = style) {
    BaseText("Button")
}
BaseText("Different text that uses the same style parameter", style = style)

// modifier styleable
val columnStyleState = remember { MutableStyleState(null) }
Column(
    Modifier.styleable(columnStyleState, style)
) {
    BaseText("Column")
}
val rowStyleState = remember { MutableStyleState(null) }
Row(
    Modifier.styleable(rowStyleState, style)
) {
    BaseText("Row")
}

添加多个样式属性

您可以通过在每行中设置不同的属性来添加多个样式属性:

BaseButton(
    onClick = { },
    style = {
        background(Color.Blue)
        contentPaddingStart(16.dp)
    }
) {
    BaseText("Button")
}

与基于修饰符的样式设置不同,样式中的属性不是累加的。样式会采用一个样式块中属性列表中的最后一个设置值。在以下示例中,背景设置了两次,TealColor 是应用的背景。对于内边距,contentPaddingTop 会替换 contentPadding 设置的上内边距,而不会合并这些值。

BaseButton(
    style = {
        background(Color.Red)
        // Background of Red is now overridden with TealColor instead
        background(TealColor)
        // All directions of padding are set to 64.dp (top, start, end, bottom)
        contentPadding(64.dp)
        // Top padding is now set to 16.dp, all other paddings remain at 64.dp
        contentPaddingTop(16.dp)
    },
    onClick = {
        //
    }
) {
    BaseText("Click me!")
}

设置了两种背景颜色和两种 contentPadding 替换的按钮
图 1. 设置了两种背景颜色和两种 contentPadding 替换的按钮。

合并多个样式对象

您可以创建多个 Style 对象,并将它们传递到可组合函数的 style 参数中。

val style1 = Style { background(TealColor) }
val style2 = Style { contentPaddingTop(16.dp) }

BaseButton(
    style = style1 then style2,
    onClick = {

    },
) {
    BaseText("Click me!")
}

设置了背景色和 contentPaddingTop 的按钮
图 2. 设置了背景颜色和 contentPaddingTop 的按钮。

如果多个样式指定了同一属性,则选择最后设置的属性。由于样式中的属性不是累加的,因此传入的最后一个边衬区会替换初始 contentPadding 设置的 contentPaddingHorizontal。此外,最后一个背景颜色会覆盖传入的初始样式设置的背景颜色。

val style1 = Style {
    background(Color.Red)
    contentPadding(32.dp)
}

val style2 = Style {
    contentPaddingHorizontal(8.dp)
    background(Color.LightGray)
}

BaseButton(
    style = style1 then style2,
    onClick = {

    },
) {
    BaseText("Click me!")
}

在这种情况下,应用的样式具有浅灰色背景和 32.dp 内边距,但左侧和右侧内边距的值为 8.dp

具有 contentPadding 的按钮,该属性会被不同的样式替换
图 3. 具有 contentPadding 的按钮,该属性会被不同的样式替换。

样式继承

某些样式属性(例如 contentColor 和与文本样式相关的属性)会传播到子可组合项。为子可组合项设置的样式会覆盖该特定子可组合项的继承父样式。

使用 Style、styleable 和 direct 参数进行样式传播
图 4. 使用 Stylestyleable 和直接参数进行样式传播。
优先级 方法 影响
1(最高) 可组合项上的直接实参 替换所有内容;例如,Text(color = Color.Red)
2 样式参数 本地样式覆盖 Text(style = Style { contentColor(Color.Red)}
3 修饰符链 Modifier.styleable{ contentColor(Color.Red)
4(最低) 父级样式 对于可从父级继承的属性(排版/颜色),从父级传递下来。

父级样式

您可以从父可组合函数设置文本属性(例如 contentColor),这些属性会传播到所有子 Text 可组合函数。

val styleState = remember { MutableStyleState(null) }
Column(
    modifier = Modifier.styleable(styleState) {
        background(Color.LightGray)
        val blue = Color(0xFF4285F4)
        val purple = Color(0xFFA250EA)
        val colors = listOf(blue, purple)
        contentBrush(Brush.linearGradient(colors))
    },
) {
    BaseText("Children inherit", style = { width(60.dp) })
    BaseText("certain properties")
    BaseText("from their parents")
}

子可组合项的属性继承
图 5. 子可组合项的属性继承。

子级对属性的替换

您还可以为特定 Text 可组合函数设置样式。如果父可组合项设置了样式,则子可组合项上设置的样式会覆盖父可组合项的样式。

val styleState = remember { MutableStyleState(null) }
Column(
    modifier = Modifier.styleable(styleState) {
        background(Color.LightGray)
        val blue = Color(0xFF4285F4)
        val purple = Color(0xFFA250EA)
        val colors = listOf(blue, purple)
        contentBrush(Brush.linearGradient(colors))
    },
) {
    BaseText("Children can ", style = {
        contentBrush(Brush.linearGradient(listOf(Color.Red, Color.Blue)))
    })
    BaseText("override properties")
    BaseText("set by their parents")
}

子可组合项会替换父属性
图 6. 子可组合项会替换父属性。

实现自定义样式属性

您可以使用 StyleScope 的扩展函数创建映射到现有样式定义的自定义属性,如以下示例所示:

fun StyleScope.outlinedBackground(color: Color) {
    border(1.dp, color)
    background(color)
}

在样式定义中应用此新属性:

val customExtensionStyle = Style {
    outlinedBackground(Color.Blue)
}

不支持创建新的可设置样式的属性。如果您的使用情形需要此类支持,请提交功能请求

读取 CompositionLocal

一种常见模式是将设计系统令牌存储在 CompositionLocal 中,以便在无需将变量作为参数传递的情况下访问这些变量。样式可以访问 CompositionLocal 以检索样式中的全局系统值:

val buttonStyle = Style {
    contentPadding(12.dp)
    shape(RoundedCornerShape(50))
    background(Brush.verticalGradient(LocalCustomColors.currentValue.background))
}