Compose中的主题

5,720 阅读4分钟

设置Material主题

Material主题主要包含三个属性: 颜色、排版和形状,API如下:

@Composable
fun MaterialTheme(
    colors: Colors = MaterialTheme.colors, // 颜色集合
    typography: Typography = MaterialTheme.typography, // 排版集合
    shapes: Shapes = MaterialTheme.shapes, // 形状集合
    content: @Composable () -> Unit // 要展示的内容
)

颜色

class Colors(
    primary: Color, // 主颜色,屏幕和元素都用这个颜色
    primaryVariant: Color, // 用于区分主颜色,比如app bar和system bar
    secondary: Color, // 强调色,悬浮按钮,单选/复选按钮,高亮选中的文本,链接和标题
    secondaryVariant: Color, // 用于区分强调色
    background: Color, // 背景色,在可滚动项下面展示
    surface: Color, // 表层色,展示在组件表层,比如卡片,清单和菜单(CardView,SheetLayout,Menu)等
    error: Color, // 错误色,展示错误信息,比如TextField的提示信息
    onPrimary: Color, // 在主颜色primary之上的文本和图标的颜色
    onSecondary: Color, // 在强调色secondary之上的文本和图标的颜色
    onBackground: Color, // 在背景色background之上的文本和图标的颜色
    onSurface: Color, // 在表层色surface之上的文本和图标的颜色
    onError: Color, // 在错误色error之上的文本和图标的颜色
    isLight: Boolean // 是否是浅色模式
) 

排版

class Typography internal constructor(
    val h1: TextStyle, // 一级标题
    val h2: TextStyle,
    val h3: TextStyle,
    val h4: TextStyle,
    val h5: TextStyle,
    val h6: TextStyle,
    val subtitle1: TextStyle, // 一级副标题
    val subtitle2: TextStyle,
    val body1: TextStyle, // 一级内容
    val body2: TextStyle,
    val button: TextStyle, // 按钮使用
    val caption: TextStyle, // 说明文字
    val overline: TextStyle // 页眉/页脚
)

这里主要定义了各级标题/副标题,以及内容区的字体属性,至于TextStyle的具体属性可以看这里: Compose中的Text

形状

class Shapes(
    
    // 小组件使用的形状,比如: Button,SnackBar,悬浮按钮等
    val small: CornerBasedShape = RoundedCornerShape(4.dp),
    
    // 中组件使用的形状,比如Card(就是CardView),AlertDialog等
    val medium: CornerBasedShape = RoundedCornerShape(4.dp),
    
    // 大组件使用的形状,比如ModalDrawer或者ModalBottomSheetLayout(就是抽屉布局和清单布局)
    val large: CornerBasedShape = RoundedCornerShape(0.dp)
)

使用

现在让我们来try try,首先我们来定义颜色,代码如下:

// 定义深色模式下使用的颜色
private val DarkColorPalette = darkColors(
    primary = Color.White,
    onPrimary = Color.Red,
    secondary = Color.Black,
    surface = Color.Green,
)
​
// 定义浅色模式下使用的颜色
private val LightColorPalette = lightColors(
    primary = Color.Black,
    onPrimary = Color.Yellow,
    secondary = Color.White,
    surface = Color.Blue
)

代码很简单,只定义了深色和浅色模式下的几种颜色,其实Compose已经内置有颜色可供使用了,比如代码中的darkColorslightColors,可以自行翻阅源码查看,里面只是提供了颜色参考。比如darkColors:

fun darkColors(
    primary: Color = Color(0xFFBB86FC),
    primaryVariant: Color = Color(0xFF3700B3),
    secondary: Color = Color(0xFF03DAC6),
    secondaryVariant: Color = secondary,
    background: Color = Color(0xFF121212),
    surface: Color = Color(0xFF121212),
    error: Color = Color(0xFFCF6679),
    onPrimary: Color = Color.Black,
    onSecondary: Color = Color.Black,
    onBackground: Color = Color.White,
    onSurface: Color = Color.White,
    onError: Color = Color.Black
)

接着,我们来定义形状,代码如下:

val Shapes = Shapes(
    small = RoundedCornerShape(4.dp),
    medium = RoundedCornerShape(8.dp),
    large = RoundedCornerShape(16.dp)
)

我们根据不同大小的形状,设置不同的圆角,当然你也可以设置颜色、四个角的单独尺寸等。

接着,我们来定义排版,代码如下:

val Typography = Typography(
    body1 = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.Normal,
        fontSize = 16.sp
    ),
​
    button = TextStyle(
        fontFamily = FontFamily.Default,
        fontWeight = FontWeight.W500,
        fontSize = 14.sp
    ),
)

我们只定义了body1button,有兴趣的也可以试试其他的(感觉这属性没什么卵用)。

好,现在让我们用起来,我们添加如下代码:

@Composable
fun ComposeTourTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
    // 根据不同模式使用不同颜色值
    val colors = if (darkTheme) {
        DarkColorPalette
    } else {
        LightColorPalette
    }
​
    MaterialTheme(
        colors = colors,
        typography = Typography,
        shapes = Shapes,
        content = content
    )
}

逻辑很简单,就只直接调用MaterialTheme来传入我们定义的颜色、形状和排版,这里可以使用isSystemInDarkTheme()来判断当前是否是深色模式,如果是深色模式,就使用深色主题,否则就用浅色主题。

最后,在Activity里面调用:

setContent {
    ComposeTourTheme {
        Column(
            modifier = Modifier.fillMaxSize(),
            verticalArrangement = Arrangement.Center,
            horizontalAlignment = Alignment.CenterHorizontally
        ) {
            // 添加一个Card
            Card(modifier = Modifier.padding(bottom = 16.dp)) {
                // 在Card里面添加一个CheckBox,设置边距为32dp
                Checkbox(checked = true, onCheckedChange = {}, modifier = Modifier.padding(32.dp))
            }
​
            // 添加一个Button
            Button(onClick = { Toast.makeText(this@MainActivity, "click button", Toast.LENGTH_SHORT).show() }) {
                Text(text = "Button")
            }
        }
    }
}

很简单,我们添加一个Card(就是CardView),在Card里面添加一个CheckBox,然后添加一个Button。现在让我们来看看效果:

主题展示

可以看到,切换了深色主题后,颜色会自动跟着改变,并且Card的背景圆角和Button的背景圆角也是不一样的,这是因为Card默认用的是medium尺寸,而Button默认用的是small尺寸。当然,其他属性你也可以自己去尝试。

一句话,主题就是在形状、颜色和排版方面,给app一个宏观的,粗粒度的统一,可以快速切换app的整体风格,当然,如果你要具体到每一个控件,那么主题是不适用的,你需要针对每个控件去对Modifier做具体定制,这里不再废话。