样式与主题

样式与主题:保留第四版正文机制,以所有者—状态—结果合同、事件轨迹和章专属故障完成可重放验收。

学习目标

  • 能沿“样式与主题”的用户事件解释Android组件、状态所有者、线程与销毁边界。
  • 能围绕“用 Style 和 Theme 统一 App 视觉风格——理解主题继承、Material Design 属性,以及深色模式适配。”改出一个可运行结果,并用前后状态而非组件数量验收。
  • 能在旋转、进程重建、拒权、离线或无效输入中选择适用反例,定位首个状态分叉。
  • 能用双视口、大字体、焦点顺序、状态语义和对比度截图独立重放结论,并标明第四版机制与现代targetSdk政策的边界。

你的 App 看起来像打了补丁

同一个 App,按钮有的蓝色有的灰色,文字大小不统一——为什么?因为每个 View 的属性都是手写硬编码的,没有统一管理。Style 和 Theme 就是解决这个问题的——定义一套"视觉规则",所有 View 照着来。

Style——View 的"穿搭模板"

<!-- res/values/styles.xml -->
<style name="PrimaryButton" parent="Widget.Material3.Button">
    <item name="android:textColor">@color/white</item>
    <item name="backgroundTint">@color/primary</item>
    <item name="cornerRadius">8dp</item>
    <item name="android:textSize">16sp</item>
</style>

然后一个按钮只需引用这个 style:

 

改了 style 里的 cornerRadius 从 8dp 到 12dp → 所有引用这个 style 的按钮自动变成圆角 12dp。统一改一处,全 App 生效

Style 继承

<!-- 继承父 style(点记法) -->
<style name="PrimaryButton.Outlined">
    <item name="backgroundTint">@android:color/transparent</item>
    <item name="strokeColor">@color/primary</item>
</style>

PrimaryButton.Outlined 继承 PrimaryButton 的所有属性,只覆盖要改的——描边按钮只有背景变透明+加描边,文字大小和圆角沿用父 style。

Theme——整个 App 的"皮肤"

Theme 也是 Style,但作用于 Application 或 Activity 级别:

<!-- res/values/themes.xml -->
<style name="Theme.MyApp" parent="Theme.Material3.Light.NoActionBar">
    <item name="colorPrimary">@color/blue_500</item>
    <item name="colorOnPrimary">@color/white</item>
    <item name="android:statusBarColor">@color/blue_700</item>
</style>

在 Manifest 中应用:

<application android:theme="@style/Theme.MyApp">
Style 改一个控件,Theme 管整个界面;同名属性看优先级左:style 继承链作用于单个 View 右:theme 作用域覆盖整个 Activity/App① Style 继承链:父 → 自定义 → 单个 View继承父属性,只覆盖 textColor应用到这一个 ButtonWidget.Material3.Button父 style(系统内置)定义按钮的默认外观圆角 / 内边距 / 字号 …MyButton自定义 style(parent 指向父)parent=Widget.Material3.Button只覆盖 textColor,其余沿用父<Button style="@style/MyButton">应用到单个 Viewstyle 只作用在这一个按钮上② Theme 作用域:声明一次,渗透整个界面<application android:theme="@style/Theme.MyApp">声明一次(或 Activity 级)→ colorPrimary 等属性全局生效渗透到整个界面整个 Activity 界面Button?attr/colorPrimaryTextView?attr/colorPrimarySwitch?attr/colorPrimary③ 同名属性最终值优先级(左低右高,后者覆盖前者)theme被右侧覆盖<style被右侧覆盖<布局内联 xml被右侧覆盖<代码 setXxx优先级最高
Style 是「单个 View 的穿搭模板」:用点记法/parent 继承父 style,只覆盖少量属性,再以 style="@style/MyButton" 套到某个控件上。Theme 是「整个界面的皮肤」:在 android:theme 声明一次便作用于整个 Activity/App,控件用 ?attr/colorPrimary 从主题取色。同名属性最终值优先级: theme < style < 布局内联 xml < 代码 setXxx——后者覆盖前者。

深色模式

<!-- res/values-night/themes.xml -->
<style name="Theme.MyApp" parent="Theme.Material3.Dark.NoActionBar">
    <item name="colorPrimary">@color/blue_200</item>
    <!-- 深色背景、浅色文字由父主题自动处理 -->
</style>

values-night/ 目录——系统切换到深色模式时自动使用这里的资源。Material Design 3 的 Dark 主题帮你处理了大部分颜色映射,你只需要微调品牌色即可。

分步1 / 4

① 定义 Theme

res/values/themes.xml 中定义 <style name="Theme.MyApp" parent="Theme.Material3.Light.NoActionBar">,设置 colorPrimarycolorOnPrimarystatusBarColor 等属性——这些颜色会渗透到所有 Material 组件中。

容易踩的坑

小结

  • Style 复用 View 属性 → 消除 XML 重复、统一视觉
  • Theme 是作用在 Application/Activity 级别的 Style → 整体皮肤
  • Style 继承(点记法)→ 子 Style 覆盖少量属性,其余继承父
  • 深色模式:values-night/ 目录提供暗色版本的资源和主题
  • 内联属性优先级最高 → 统一管理时应去掉 View 上的硬编码

练习

问题 1:“第21章 Styles and Themes”覆盖哪些正式节点和项目主线?

问题 2:怎样建立本页最小可执行实验?

问题 3:为什么只在正常点击路径运行不能证明完成?

问题 4:怎样设计能推翻当前实现的反例?

问题 5:从第4版语境迁移到现代目标SDK时如何控制变量?

问题 6:本页达到独立交接标准需要什么?

名词解释

本章出现的专业名词,用大白话再讲一遍。

Style

一组 View 属性的集合——定义在 res/values/styles.xml 中,通过 style="@style/xxx" 引用。复用 View 属性消除 XML 重复代码。

Theme

作用在 Application/Activity 级别的特殊 Style。定义全局视觉属性——主色、强调色、背景色、状态栏颜色等。决定整个 App 的视觉基调。

“样式与主题”不使用未获授权的纸书正文;InformIT出版信息授权电子版完整目录只用于确认第四版32章、269个正式目录节点和时代语境,第四版官方勘误用于识别工具链变更。下列中文解释、图示、交互、代码与练习均为独立教学重写,平台行为再以Android Developers的一手文档复核。

为什么“样式与主题”必须回到可观察状态

“样式与主题”的学习结果不是记住类名,而是能预测“用 Style 和 Theme 统一 App 视觉风格——理解主题继承、Material Design 属性,以及深色模式适配。”在一次输入、一次重建和一次失败中的不同状态,并指出哪条Android合同产生差异。

第四版机制逐项深读

21. Styles and Themes

在“样式与主题”中,验证“21. Styles and Themes”在日夜主题、不同密度和字体缩放下比较像素边界、对比度与状态反馈。

Color Resources

在“样式与主题”中,验证“Color Resources”在日夜主题、不同密度和字体缩放下比较像素边界、对比度与状态反馈。

Styles

在“样式与主题”中,“Styles”通过资源限定符和主题属性把内容与设备配置解耦;最终值由资源匹配和主题继承共同决定。

Themes

在“样式与主题”中,验证“Themes”在日夜主题、不同密度和字体缩放下比较像素边界、对比度与状态反馈。

Adding Theme Colors

在“样式与主题”中,“Adding Theme Colors”通过资源限定符和主题属性把内容与设备配置解耦;最终值由资源匹配和主题继承共同决定。

Overriding Theme Attributes

在“样式与主题”中,“Overriding Theme Attributes”的复用不能吞掉状态语义;pressed、focused、disabled与checked都要有可辨反馈。

Modifying Button Attributes

在“样式与主题”中,分析“Modifying Button Attributes”先冻结设备API、targetSdk和输入,再沿回调与数据流寻找首个状态分叉,不能只描述最终页面。

For the More Curious: More on Style Inheritance

在“样式与主题”中,验证“For the More Curious: More on Style Inheritance”在日夜主题、不同密度和字体缩放下比较像素边界、对比度与状态反馈。

For the More Curious: Accessing Theme Attributes

在“样式与主题”中,分析“For the More Curious: Accessing Theme Attributes”要追踪资源ID、限定目录、密度缩放与属性解析,避免把预览器结果当成所有设备的合同。

“样式与主题”验收回顾

“样式与主题”只有在双视口、大字体、焦点顺序、状态语义和对比度截图能够从相同基线再次得到相同断言时才通过;第四版机制与现代平台政策分别记录,不用新API名称掩盖旧行为。

← 上一页:音频播放与单元测试 · 下一页:XML drawable →

章专属可重放状态实验

先预测“切换 day/night、组件状态与局部 theme overlay”发生后,Resources、Theme 与 View 属性解析器应怎样改变主题属性、style 父级、局部覆盖、日夜模式和最终像素;再操作三个实验。第四版示例与当前 Android 政策分别记录,实验不把新 API 名称倒填为原书内容。

实验一:所有者—状态—结果合同

选择任一正式目录节点和正常/边界场景,检查它是否真的进入本章状态合同。目录标题只有同时出现在解释、可视状态和交付证据中才算覆盖。

Owner · state · observable result

样式与主题:状态合同

让颜色、style、theme 与属性覆盖形成可解释的资源继承链

验证场景

第四版正式目录节点

styles-themes · 正常任务

21. Styles and Themes固定 SDK、设备配置和初始状态,触发“切换 day/night、组件状态与局部 theme overlay”

状态所有者Resources、Theme 与 View 属性解析器
受控状态主题属性、style 父级、局部覆盖、日夜模式和最终像素
触发事件切换 day/night、组件状态与局部 theme overlay

冻结入口:21. Styles and Themes

记录Resources、Theme 与 View 属性解析器的初始主题属性、style 父级、局部覆盖、日夜模式和最终像素

观察:属性解析链、资源 ID、日夜截图、对比度和状态断言中的“21. Styles and Themes”轨迹

预期:由Resources、Theme 与 View 属性解析器提交主题属性、style 父级、局部覆盖、日夜模式和最终像素,并持续满足“内容语义与主题解耦,最终颜色保持对比度和状态可辨性”

实验二:事件与生命周期轨迹

沿五次转换逐步执行“切换 day/night、组件状态与局部 theme overlay”。每一步只允许Resources、Theme 与 View 属性解析器按职责提交状态,并持续核对“内容语义与主题解耦,最终颜色保持对比度和状态可辨性”。

Deterministic event replay

样式与主题:事件轨迹

选择一次状态转换1 / 5

不变量:内容语义与主题解耦,最终颜色保持对比度和状态可辨性

交付证据:属性解析链、资源 ID、日夜截图、对比度和状态断言

实验三:章专属反例与同输入恢复

注入“在布局写死颜色,夜间主题下文字与背景对比度不足”,保存第一个偏离点;撤销后以完全相同的 SDK、设备状态和用户事件重放。只有属性解析链、资源 ID、日夜截图、对比度和状态断言一起恢复才算修复。

Fault · cancel · restore

样式与主题:反例与恢复

故障:在布局写死颜色,夜间主题下文字与背景对比度不足

1. 冻结输入一致

第 1 次使用相同 SDK、设备配置、初始状态与用户事件

2. 注入边界一致

保持正常输入不变,仅注入“在布局写死颜色,夜间主题下文字与背景对比度不足”

3. 检查所有者一致

内容语义与主题解耦,最终颜色保持对比度和状态可辨性

4. 核对结果一致

属性解析链、资源 ID、日夜截图、对比度和状态断言

讨论

评论区加载中…