66、主题与样式:Theme与Style定义、夜间模式、Material You动态颜色

说实话,主题和样式这块,是很多Android开发者容易忽略的“基本功”。

我刚入行那会儿,也觉得不就是改改颜色嘛,有什么好学的?直到接手一个老项目,里面光colors.xml就写了上千行,改个夜间模式要翻遍十几个布局文件……嗯,从那以后,我再也不敢小看Theme和Style了。

Style是什么?说白了就是“样式模板”

Style,就是一组属性的集合。你可以把它理解成一个“样式模板”。

比如你有一个按钮,想让它圆角、蓝色、文字白色。你当然可以在每个Button里写一遍。但更好的做法是——定义一个Style,然后到处引用。

<!-- res/values/styles.xml -->
<style name="PrimaryButton">
    <item name="android:layout_width">match_parent</item>
    <item name="android:layout_height">48dp</item>
    <item name="android:background">@drawable/btn_primary_bg</item>
    <item name="android:textColor">@android:color/white</item>
    <item name="android:textSize">16sp</item>
</style>

然后在布局里引用:

<Button
    style="@style/PrimaryButton"
    android:text="登录" />

你看,代码瞬间清爽了。而且后续要改样式,只需要改一个地方。

我的习惯:我会把Style按组件类型命名,比如PrimaryButtonSecondaryButtonInputField。这样团队里谁都能一眼看懂。

Theme是什么?它是全局的“氛围”

Theme和Style很像,但作用范围不同。Theme作用于整个Activity或Application,它定义的是全局的“氛围”。

比如你希望整个App的主色调是蓝色,文字是深灰色,背景是浅灰色。这些就放在Theme里。

<!-- res/values/themes.xml -->
<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="colorPrimary">@color/blue_500</item>
    <item name="colorOnPrimary">@color/white</item>
    <item name="colorSecondary">@color/teal_200</item>
    <item name="android:colorBackground">@color/gray_50</item>
    <item name="android:textColorPrimary">@color/gray_900</item>
</style>

然后在AndroidManifest.xml里引用:

<application
    android:theme="@style/Theme.MyApp">
    ...
</application>

这样整个App就统一了。你想想看,如果每个页面都单独写颜色,那维护起来得多痛苦?

夜间模式:别让用户晚上“亮瞎眼”

夜间模式,说白了就是根据系统设置,自动切换一套暗色主题。

实现起来其实很简单:

  1. res/values里定义日间主题
  2. res/values-night里定义夜间主题
  3. 系统会自动根据时间或用户设置切换

举个例子,日间主题:

<!-- res/values/themes.xml -->
<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="colorPrimary">@color/blue_500</item>
    <item name="android:colorBackground">@color/white</item>
    <item name="android:textColorPrimary">@color/black</item>
</style>

夜间主题:

<!-- res/values-night/themes.xml -->
<style name="Theme.MyApp" parent="Theme.MaterialComponents.DayNight.NoActionBar">
    <item name="colorPrimary">@color/blue_200</item>
    <item name="android:colorBackground">@color/black</item>
    <item name="android:textColorPrimary">@color/white</item>
</style>
我曾经踩过的坑:在夜间模式下,有些图片背景是白色的,结果在暗色背景下完全看不见。后来我养成了习惯——所有图片资源都准备两套,或者用Vector Drawable配合主题色。

Material You动态颜色:让App“随壁纸变色”

Material You是Android 12引入的新特性。它会从用户的壁纸中提取主色调,然后自动生成一套配色方案。

说白了,就是你的App颜色会跟着用户的壁纸走。每个人看到的App颜色都不一样。

启用方式很简单:

<!-- res/values/themes.xml -->
<style name="Theme.MyApp" parent="Theme.Material3.DynamicColors.DayNight">
    ...
</style>

然后在代码里调用:

// 在Application或Activity的onCreate中调用
DynamicColors.applyToActivityIfAvailable(this);

这样你的App就自动支持动态颜色了。你不需要手动定义任何颜色,系统会帮你搞定。

注意:Material You只在Android 12+上生效。低版本会回退到你定义的主题色。所以一定要在colors.xml里保留一套备用颜色。

知识体系总览

下面这张图,是我自己整理的主题与样式知识结构。你看一眼就能明白它们之间的关系:

Android 主题与样式知识体系 Theme(主题) Style(样式) 夜间模式 Material You 动态颜色 自定义主题 values-night 目录 自动切换暗色主题 DynamicColors API 从壁纸提取颜色 colorPrimary / colorSecondary 备用颜色方案

实战建议:从零开始搭建主题系统

我个人习惯,在新项目一开始就搭好主题框架。具体步骤:

  1. 定义基础颜色:在colors.xml里定义主色、辅色、背景色、文字色
  2. 定义Style:把常用的按钮、输入框、卡片都定义成Style
  3. 定义Theme:在themes.xml里引用颜色,并设置全局属性
  4. 准备夜间模式:在values-night里覆盖颜色值
  5. 启用Material You:在代码里调用DynamicColors.applyToActivityIfAvailable()

这样一套下来,你的App就具备了完整的主题能力。后续加新页面,直接引用Style和Theme颜色就行,几乎不用再写重复代码。

一个小技巧:在开发阶段,可以用@color/引用颜色,而不是硬编码。这样后期改主题色,只需要改colors.xml一个文件。

好了,关于主题与样式,我就讲这么多。记住一句话:好的主题系统,能让你的App看起来像是一个整体,而不是东拼西凑的碎片。


公众号:蓝海资料掘金营,微信deep3321