6.2 从Style到Theme的复用阶梯


6.2 从 Style 到 Theme 的复用阶梯

本节摘要:Style 是控件属性的集合,Theme 是作用在上下文上的 Style;同一个机制,两种用法。本节为猜数字 App 定义按钮样式与全局主题,讲清 parent 继承、主题属性点(?attr/)引用与 Material 主题的调色板,最后用 overlay 做出"胜利页面"局部变体。

Style:把重复属性收拢成一份

猜数字 App 三个按钮的 XML 长得一模一样, textSize、cornerRadius、高度全是复读。第一步收拢——在 values/styles.xml 里定义:

<style name="Widget.GuessButton" parent="Widget.Material3.Button"> <item name="android:textSize">16sp</item> <item name="android:layout_height">56dp</item> <item name="cornerRadius">28dp</item> <item name="backgroundTint">?attr/colorPrimary</item> </style>

控件上引用,五行变一行:

<Button style="@style/Widget.GuessButton" android:layout_width="match_parent" android:text="@string/btn_guess" />

parent 声明继承:从 Material 按钮基样式出发只改差异项,未列出的属性全部继承。省略 parent 则从空开始,你会丢掉所有 Material 默认(水波纹、按压态、无障碍最小尺寸),自己一行行补到怀疑人生。继承还可以用点号简写:Widget.GuessButton.Outline 自动以 Widget.GuessButton 为父,构成样式族的树状谱系。

Theme:给整个上下文化妆

Style 管一个控件,Theme 管一整个 Activity 或 Application 的默认氛围。看主题清单:

<style name="Theme.GuessNumber" parent="Theme.Material3.DayNight.NoActionBar"> <item name="colorPrimary">@color/primary</item> <item name="colorOnPrimary">@color/white</item> <item name="android:colorBackground">@color/page_bg</item> <item name="textAppearanceBodyLarge">@style/Text.Guess.Body</item> </style>

在清单文件里挂到应用或某个 Activity:

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

关键认知在这里落地:Theme 与 Style 在语法上是同一种东西——都是属性集合。区别只在于作用点:用 style= 挂到控件上叫 Style,挂到上下文上、让全局控件默认继承的叫 Theme。主题里定义的 colorPrimary 不是给谁直接显示的,而是发放给整个界面的一套语义色票,任何控件问"主色是什么"都得到同一答案。

控件问主题要属性的语法是 ?attr/——样式里的 backgroundTint="?attr/colorPrimary" 就是这一问。它与 @color/primary 的差别是间接性:@ 引用写死某具体颜色,?attr 引用"当前上下文主题的这项属性",主题换了它自动跟随。样式里尽量用 ?attr,让同一份样式在不同主题下呈现不同面貌,这是夜间模式能一行不改全局生效的底层机关。

overlay:不换主题的局部变体

猜数字的胜利页想要绿底庆祝氛围,但只此一屏。做法不是新建全套主题,而是 overlay——继承原主题只覆盖差异:

<style name="ThemeOverlay.GuessNumber.Win" parent=""> <item name="colorPrimary">@color/success</item> <item name="android:colorBackground">@color/win_bg</item> </style>
// 只给胜利对话框的上下文套上 overlay val themedContext = ContextThemeWrapper(this, R.style.ThemeOverlay_GuessNumber_Win)

overlay 的语义是叠加不改全局:它包裹出一个临时的主题上下文,这个上下文里诞生的控件看到的是覆盖后的色票,Activity 其余部分毫发无损。parent="" 置空是 overlay 的标志写法,防止它自立门户。

图:复用阶梯与引用语法对比

图:复用阶梯与引用语法对比

本节要点回顾

  • Style 与 Theme 同体不同用:挂控件是样式,挂上下文是主题。
  • parent 从 Material 基样式出发:省略 parent 会丢掉全部默认细节。
  • ?attr 是对主题的提问:样式里用它代替写死颜色,换主题自动跟随。
  • overlay 做局部变体:ContextThemeWrapper 包一层,全局不受扰。
  • 点号继承构建样式族:基样式加差异,谱系即文档。

两个实战追问

老项目的 Theme.AppCompat 还能见吗? 大量存在,它属于 Material Component 之前的 AppCompat 体系。两套主题的属性名不完全通用(colorPrimary 相同、其余有差异),接手老项目时先看主题 parent 属于哪个体系再查属性表,能省下不少"属性不生效"的困惑。

样式放 styles.xml 还是另外的文件? 文件名随意(values 目录合并语义在 6.1 节讲过),但团队要有统一约定——常见做法是 styles 放通用、themes 放主题、按模块再分文件。文件组织是给人的导航图,机器不关心,你和你三个月后的同事关心。


作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U