Android Styling System
1. 背景
本文主要对安卓样式系统进行分析,包括主题背景和样式两大主题,希望可以形成知识体系,并且应用到日常开发过程中,让布局和样式更加灵活,提高代码复用性并保持代码库的精简和易维护性。
2. 主题背景与样式的区别
2.1 主题背景 != 样式
2.1.1 共同点
- 使用相同的
<style>
语法 - 使用键值对 (Key-Value) 来存储数据,键 (Key) 代表属性,值 (Values) 代表资源
2.1.2 不同点
- 服务的目的截然不同 / 针对的对象或应用场景不同
2.2 样式
2.2.1 样式里面有什么
- 样式是View 属性 (View Attributes) 值的集合
- 一个样式对应一种类型的 Widget
- 可以在多个 View 中复用同一个样式,而且还容易维护
2.2.2 使用方法
- 一个 View 只能使用一个样式
2.2.3 范围
- 样式只有在使用它的 View 上才起作用,子View不起作用
2.3 主题背景
2.3.1 什么是主题背景?
- 一组命名的资源的集合
- 可以被样式或者布局文件等引用
- 由 Map<theme attribute, resource> 结构组成
- 类似于接口 (Interface)
- 主题背景属性:一种对 Android 资源的语义名称 (Sematic name);对一个值的命名,为这些标有名字的资源提供了具体的值
2.3.2 使用方法
- 可以把一个主题背景设置给一个组件(Application/Activity/View/ViewGroup)
- 可以使用 ContextThemeWrapper 类把一个主题背景设置到已经存在的 Context 上,也可使用 inflate 方法创建布局
- 可以通过引用主题背景属性来创建灵活的 Widget
- 通过使用 ?attr/themeAttributeName 语法获得此主题背景中的语义属性代表的值
2.3.3 范围
- 任何一个带有 Context (如 Activity, View or ViewGroup) 的对象 (Object) 都可以通过访问 Context 的属性来访问主题背景
- 以树的形式组织而成,把主题背景设置到一个树状结构的任意一层,此层及下一层都会受到影响,这种功能仅在初始化布局的时候生效
- 在初始化布局之前:调用 Context 提供的 setTheme 方法;主题背景提供的 applyStyle 方法
- 布局初始化完毕之后,再调用 setTheme 或者 applyStyle 方法,不会生效
3.常见的主题背景属性
3.1 还可以使用哪些主题背景属性的功能呢?
- 颜色
- 大小
- Drawables
- TextAppearance
- 形状
- 按钮风格
- Floats
3.2 命名空间
- 由 Android 平台定义的,需要使用 android 命名空间来引用由它们自己定义的属性 (类似于布局中使用 View 属性 android:id)
- 编译到您的应用但不是来自于静态库的属性 (AppCompact 或者 MDC) ,使用它们时不需要命名空间 (类似于布局中使用 app:baz)
- 平台跟库有时候定义了相同的属性,优先使用非平台版本的属性,它们可以被所有级别的 API 使用
3.3 如何自定义一个主题背景
3.3.1 在 attrs.xml 中定义主题背景属性:
3.3.2 在不同的主题背景中使用不同的值:
3.3.3 给两个界面使用的布局文件中使用主题背景属性:
4.主题背景属性
创建更少的布局或样式,以隔离主题背景中的修改
4.1 合格的 Colors 文件
- 通过使用主题背景属性,我们可以将语义颜色的声明从提供它们的值中区分开来
- 让使用方更清楚地了解到颜色会随主题背景而变化 (因为它们使用 ?attr/ 语法)
- 将颜色声明保持为字面值,您就可以自定义应用使用的颜色调色板,并在主题背景级别修改它们
- 您不需要创建其他布局或样式就可以更改某些颜色,您可以在相同的布局中使用不同的主题背景
4.2 始终使用?
- 直接引用颜色资源
4.3 当前发展状况
4.3.1 使用ColorStateLists实现主题化功能的方法?
- 可在选取的颜色上指定透明度值
- 这种单项 ColorStateList (即只提供单个默认颜色,而非每种状态的不同颜色),有助于减少您需要维护的颜色资源数量
4.3.2 注意事项
- 如果指定的颜色也具有 alpha 值,则 alpha 会被合并。
- 仅在 API 23 中添加了 alpha 组件,请确保使用支持此行为的 AppCompatResources.getColorStateList;并始终使用 android:alpha 命名空间,而绝不使用 app:alpha 命名空间
- 通常,我们使用简写法,将颜色设置为 Drawable,
- View 的背景是一个 Drawable,此简写把给定的颜色强转成了一个 ColorDrawable;但没有办法把 ColorStateList 转换成 Drawable (API 29 之前使用 olorStateListDrawable 解决这个问题;但有另外一种方法:请确保您的 backgroundTint 支持您的 View 所需的状态,例如,如果被禁用时需要更改。
4.4 间接使用
使用主题背景属性和 ColorStateList 将颜色分解为主题背景的方法,可使您的布局和样式更加灵活,提高代码复用性并保持代码库的精简和易维护性。
5.主题背景覆盖
5.1 范围
- 在树结构中的任何层级上设置主题背景,都不会替换当前生效的主题背景,但会将其覆盖 (Overlay)
5.2 过度重叠
- 主题背景覆盖 (Theme Overlay)
- 主题背景覆盖是限定范围的主题背景,定义的属性要越少越好,它的作用只是为了覆盖另外一个主题背景
- 主题背景覆盖通常 (但并不总是) 是没有父级的
- 可以认为在应用中可以使用两种 “类型” 主题:1.“完整” 主题背景;2.主题背景覆盖
5.3 永远存在
- 总会有一个有效的主题背景,即使您未在应用中的任何地方指定一个主题背景,您也会继承默认主题
- 绝对不应该在 View 中使用一个 “完整” 的主题背景,而应使用主题背景覆盖
5.4 成本效益
- 每次您声明 android:theme 时,您都在创建一个新的 ContextThemeWrapper,它会分配新的主题背景 (Theme) 和资源 (Resources) 实例
- 它还需要解决多层级样式化的间接引用问题
- 注意不要过度使用主题,您应该监控它们的影响,特别是在重复使用的情况下,例如: RecyclerView 项的布局或者配置文件
5.5 在上下文中使用
- 如果您在代码中使用 Context 来获取资源 (Resource),请确保您使用的是正确的 Context,如
someView.background = AppCompatResources.getDrawable(requireContext(), R.drawable.foo)
- 应使用离资源 (Resource) 最近的 Context:
someView.background = AppCompatResources.getDrawable(someView.context, R.drawable.foo)
5.6 误用
- 切勿使用 Application Context 加载可使用的资源
- Application Context 不保留任何主题背景相关信息,您在 manifest 中设置的主题背景仅用作未明确设置主题背景的 Activity 的默认选择
5.7 强调
6.总结
本文主要讲述了 Android样式系统的知识体系,介绍如何使用主题背景和主题背景属性来实现对资源的引用,可帮助实现应用的多主题开发支持。掌握这个知识点,有利于我们开发高质量的软件。祝大家工作顺利。
参考链接: Android 样式系统 | 主题背景覆盖.