样式与主题
样式与主题:保留第四版正文机制,以所有者—状态—结果合同、事件轨迹和章专属故障完成可重放验收。
学习目标
- 能沿“样式与主题”的用户事件解释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">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 主题帮你处理了大部分颜色映射,你只需要微调品牌色即可。
① 定义 Theme
在 res/values/themes.xml 中定义 <style name="Theme.MyApp" parent="Theme.Material3.Light.NoActionBar">,设置 colorPrimary、colorOnPrimary、statusBarColor 等属性——这些颜色会渗透到所有 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”
冻结入口: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
样式与主题:事件轨迹
不变量:内容语义与主题解耦,最终颜色保持对比度和状态可辨性
交付证据:属性解析链、资源 ID、日夜截图、对比度和状态断言
实验三:章专属反例与同输入恢复
注入“在布局写死颜色,夜间主题下文字与背景对比度不足”,保存第一个偏离点;撤销后以完全相同的 SDK、设备状态和用户事件重放。只有属性解析链、资源 ID、日夜截图、对比度和状态断言一起恢复才算修复。
Fault · cancel · restore
样式与主题:反例与恢复
故障:在布局写死颜色,夜间主题下文字与背景对比度不足
第 1 次使用相同 SDK、设备配置、初始状态与用户事件
保持正常输入不变,仅注入“在布局写死颜色,夜间主题下文字与背景对比度不足”
内容语义与主题解耦,最终颜色保持对比度和状态可辨性
属性解析链、资源 ID、日夜截图、对比度和状态断言