Android 样式系统 | 主题背景和样式
Android styling system
https://developer.android.google.cn/guide/topics/ui/look-and-feel/themes
主题背景 != 样式
样式 (Style) 里有什么?
样式是 View 属性 (View Attributes) 值的集合;一个样式对应一种类型的 Widget
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<style name="Widget.Plaid.Button.InlineAction" parent="…">
<item name="android:gravity">center_horizontal</item>
<item name="android:textAppearance">@style/TextAppearance.CommentAuthor</item>
<item name="android:drawablePadding">@dimen/spacing_micro</item>
</style>
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<Button …
android:gravity="center_horizontal"
android:textAppearance="@style/TextAppearance.CommentAuthor"
android:drawablePadding="@dimen/spacing_micro"/
使用方法
布局文件中的每一个独立的 View 都可以使用样式:
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<Button …
style="@style/Widget.Plaid.Button.InlineAction"/>
一个 View 只能使用一个样式,可以将其与 Web 技术中使用到的 CSS 样式系统相比较,CSS 样式系统可以允许一个组件使用多个 CSS 类。
范围
使用样式优先级顺序
https://medium.com/androiddevelopers/whats-your-text-s-appearance-f3a1729192d
什么是主题背景?
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<style name="Theme.Plaid" parent="…">
<item name="colorPrimary">@color/teal_500</item>
<item name="colorSecondary">@color/pink_200</item>
<item name="android:windowBackground">@color/white</item>
</style>
主题背景是由 Map<theme attribute, resource> 结构组成,这些标有名字的资源被称为主题背景属性。主题背景属性跟 View 属性不一样,这是因为它们不是特定 view 类型的属性而是对一个值的命名,其在应用中有更广泛的用途。主题背景属性为这些标有名字的资源提供了具体的值,在上面的例子中 colorPrimary 属性为这个主题背景设置了具体的值,也就是青绿色 (teal)。通过把主题背景中的资源抽象化,我们可以为不同的主题背景提供不同的值,比如: colorPrimary=orange。
/* Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
interface ColorPalette {
@ColorInt val colorPrimary
@ColorInt val colorSecondary
}
class MyView(colors: ColorPalette) {
fab.backgroundTint = colors.colorPrimary
}
这会让您使用同一套代码可渲染出不同的 MyView 效果,而无需新建构建变体。
/* Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 */
val lightPalette = object : ColorPalette { … }
val darkPalette = object : ColorPalette { … }
val view = MyView(if (isDarkTheme) darkPalette else lightPalette)
使用方法
您可以把一个主题背景设置给一个组件,这个组件可以包含 Context 或者它本身就是 Context,比如: Activity 或者是 View/ViewGroups。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<!-- AndroidManifest.xml -->
<application …
android:theme="@style/Theme.Plaid">
<activity …
android:theme="@style/Theme.Plaid.About"/>
<!-- layout/foo.xml -->
<ConstraintLayout …
android:theme="@style/Theme.Plaid.Foo">
您还可以使用 ContextThemeWrapper 类把一个主题背景设置到已经存在的 Context 上,这时候您可使用 inflate 方法创建布局。
ContextThemeWrapper https://developer.android.google.cn/reference/android/view/ContextThemeWrapper.html#ContextThemeWrapper(android.content.Context,%20int) inflate https://developer.android.google.cn/reference/android/view/LayoutInflater.html#from(android.content.Context)
主题背景的使用效果取决于您的使用方式,您可以通过引用主题背景属性来创建灵活的 Widget。不同的主题背景可以在未来再提供具体的值,比如为 View 层级结构中的某个部分设置背景颜色。
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<ViewGroup …
android:background="?attr/colorSurface">
这个语法表示通过指定的属性名称,从主题背景中获取相应的值。这种级别的解耦方式可以让我们提供不同的程序行为 (比如: 在深色模式与浅色模式下提供不同的背景颜色),而不用创建多个相似但仅有一小部分不一样的布局或者样式,它将主题中的可变元素分离了出来。
范围
任何一个带有 Context (如 Activity, View or ViewGroup) 的对象 (Object) 都可以通过访问 Context 的属性来访问主题背景。这些对象以树的形式组织而成,比如 Activity 包含 ViewGroup,而 ViewGroup 又包含 View。把主题背景设置到一个树状结构的任意一层,此层及下一层都会受到影响。比如把主题背景设置给一个 ViewGroup,此 ViewGroup 包含的所有子 View 都会受到这个主题背景的影响。(而样式恰好相反,它只对被设置的 View 起作用)
<!-- Copyright 2019 Google LLC.
SPDX-License-Identifier: Apache-2.0 -->
<ViewGroup …
android:theme="@style/Theme.App.SomeTheme">
<! - SomeTheme also applies to all child views. -->
</ViewGroup>
Context https://developer.android.google.cn/reference/android/content/Context 主题背景 https://developer.android.google.cn/reference/android/content/res/Resources.Theme.html
如果您想在浅色屏幕中获取一个由深色主题背景构成的区域,那这个功能会非常有用。更多内容请参见本系列下一篇文章,我们会在之后更新。
请注意,这种功能仅在初始化布局的时候生效。在初始化布局之前需要调用 Context 提供的 setTheme 方法或者是主题背景提供的 applyStyle 方法。布局初始化完毕之后再调用 setTheme 或者 applyStyle 方法,此时对已有的 View 不会造成任何改变。
setTheme
https://developer.android.google.cn/reference/android/content/Context#setTheme(int)
applyStyle
https://developer.android.google.cn/reference/android/content/res/Resources.Theme?hl=en#applyStyle(int,%20boolean)
不同的关注点
了解主题背景与样式的不同目的与使用方法,会让您更方便地管理样式资源。
举个例子,假设您的应用有一个蓝色主题背景,但某些 Pro 界面需要有花俏的紫色,而且您还想要提供一个调色过的深色主题。如果您只使用样式来实现这个效果,需要分别为 Pro/non-Pro 和 light/dark 创建四个不同的样式。由于样式是特定于一个视图类型 (按钮、开关等),因此您需要为应用中的每一种 View 类型创建这四个样式。
推荐阅读