在 Android 开发中,颜色的表示和获取方式非常多样,涵盖了从底层十六进制代码到高层主题属性引用的各个层面,以下是 Android 中关于颜色表示的完整指南。
1. 十六进制颜色值 (Hex Color Codes)
这是 Android 中颜色的底层表达方式。颜色由 Alpha (透明度) 、Red (红) 、Green (绿) 、Blue (蓝) 通道组成。
格式: #AARRGGBB (最常用) 或 #RRGGBB (默认不透明)。
组成:
AA: 透明度 (00=全透明, FF=不透明)。RR: 红色值。GG: 绿色值。BB: 蓝色值。
示例:
#FF000000(不透明黑色)#80FF0000(半透明红色 - 50%透明度)#FFFFFF(白色,隐含 FF 透明度)
透明度参照表:
00%=FF(不透明)
5%=F2
10%=E5
15%=D8
20%=CC
25%=BF
30%=B2
35%=A5
40%=99
45%=8c
50%=7F
55%=72
60%=66
65%=59
70%=4c
75%=3F
80%=33
85%=21
90%=19
95%=0c
100%=00(全透明)
2. 在 XML 资源中定义颜色 (Color Definition)
为了方便管理和复用,通常会在 res/values/colors.xml 文件中定义颜色。
文件路径: res/values/colors.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 定义一个不透明颜色 -->
<color name="colorPrimary">#6200EE</color>
<!-- 定义带透明度的颜色 -->
<color name="black_overlay">#66000000</color>
<!-- 引用其他颜色 -->
<color name="theme_default">@color/colorPrimary</color>
<!-- 引用 Android 中颜色:transparent是透明色 -->
<color name="transparent_default">@android:color/transparent</color>
<color name="white_default">@android:color/white</color>
</resources>
3. 在 XML 布局中使用颜色
在布局文件(如 activity_main.xml)或样式文件(styles.xml)中引用颜色。
A. 引用资源颜色 (推荐)
这是最标准的做法,支持日夜间模式切换。
xml
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/colorPrimary" />
B. 直接使用 Hex 值 (硬编码,不推荐)
仅用于临时测试,不建议在正式代码中使用。
xml
<View
android:background="#FF0000" ... />
C. 引用主题属性 (Attribute References)
这是 Material Design 和现代 Android 开发的核心。你不直接引用具体颜色,而是引用"当前主题下的某个语义颜色"。这是实现深色模式 (Dark Mode) 的关键。
语法: ?attr/属性名 或 ?android:attr/属性名
xml
<TextView
<!-- 使用当前主题定义的 "colorPrimary" 颜色 -->
android:textColor="?attr/colorPrimary"
<!-- 使用当前主题定义的背景色 -->
android:background="?android:attr/colorBackground" />
?android:attr/...:引用的是 Android 操作系统原生 的属性。?attr/...:引用的是 App 项目自身或第三方库(如 AppCompat / Material Design 库) 定义的属性。
| 特性 | ?android:attr/colorControlNormal |
?attr/colorControlNormal |
|---|---|---|
| 全称 | Android System Attribute | Application/Library Attribute |
| 定义位置 | Android SDK 源代码中 (framework) | AppCompat / Material 库中 (values/attrs.xml) |
| 引入版本 | API 21 (Android 5.0) | AppCompat 库引入时(支持旧版本) |
| 兼容性 | 差。如果在 API 21 以下的手机运行,会崩溃或找不到资源。 | 好。AppCompat 库通过"垫片"机制,让它在低版本(如 API 19)上也能工作。 |
| 命名空间 | 显式指定 android: 命名空间 |
隐式使用当前 App (通常是 res-auto) |
对于颜色、样式属性(尤其是 Material Design 相关的):
永远优先去掉 android: 前缀 。用 ?attr/colorPrimary,?attr/colorControlNormal。让 AndroidX 库帮你处理兼容性问题。
对于系统底层通用属性(很老的属性):
如果 IDE 提示找不到不带前缀的属性,或者你知道这是一个非常基础的属性(如 ?android:attr/selectableItemBackground 或 ?android:attr/listDivider),则使用 android: 前缀。
简单记忆:
只要是关于 主题色 (Primary/Secondary) 或 控件着色 (Control) 的,统统不要 加 android:。
4. 颜色状态列表 (Color State List / Selector)
ColorStateList 是 Android 中一个非常重要的概念,它本质上不是一个单一的颜色,而是一组颜色的集合。它根据 View 的当前状态(按下、聚焦、禁用、选中等)返回不同的颜色值。
A. 什么是 ColorStateList
通常在 XML 中定义(即 <selector>),用于 View 在不同状态(按下、选中、禁用等)下显示不同颜色的情况。
文件路径: res/color/button_text_color.xml
xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下状态显示红色 -->
<item android:color="#FF0000" android:state_pressed="true"/>
<!-- 禁用状态显示灰色 -->
<item android:color="#808080" android:state_enabled="false"/>
<!-- 默认状态显示黑色 -->
<item android:color="#000000"/>
</selector>
使用方式:
像普通颜色一样引用它:
xml
<Button
android:textColor="@color/button_text_color" ... />
B. Color (int) 转 ColorStateList
当你有一个单一的颜色值(int),但某个 API(如 TextView.setTextColor(ColorStateList))需要 ColorStateList 参数时,可以使用以下方式转换。这也意味着无论 View 处于什么状态,都显示同一个颜色。
Java / Kotlin:
java
int myColor = Color.RED;
// int myColor = context.getColor(R.color.my_color)
// 转换方法
ColorStateList csl = ColorStateList.valueOf(myColor);
// 使用
textView.setTextColor(csl);
C. ColorStateList 转 Color (int)
注意: 不能直接把 ColorStateList 变成一个通用的颜色,因为必须指定"状态"才能知道取哪个颜色。
通常我们需要获取当前状态下的颜色:
java
ColorStateList csl = ContextCompat.getColorStateList(context, R.color.my_selector);
// 获取 View 的当前状态数组 (例如一个 Button)
int[] state = button.getDrawableState();
// 参数2:如果找不到匹配状态,返回的默认颜色
int colorForCurrentState = csl.getColorForState(state, Color.BLACK);
// 或者手动指定获取 "按下" 状态的颜色
int colorPressed = csl.getColorForState(new int[]{android.R.attr.state_pressed}, Color.BLACK);
D. 在代码中动态创建 ColorStateList (复杂,不常用)
如果不想用 XML,可以用代码构造,但比较繁琐:
java
int[][] states = new int[][] {
new int[] { android.R.attr.state_enabled }, // enabled
new int[] { -android.R.attr.state_enabled }, // disabled
new int[] {} // default
};
int[] colors = new int[] {
Color.BLACK,
Color.GRAY,
Color.BLACK
};
ColorStateList csl = new ColorStateList(states, colors);
5. 在 Java/Kotlin 代码中获取颜色
在代码中,颜色通常以 int 类型表示(不要混淆 Resource ID 和 Color Int)。
A. 获取资源中的颜色 (标准方式)
使用 ContextCompat (推荐,兼容性最好):
java
// Java/Kotlin
int color = ContextCompat.getColor(context, R.color.colorPrimary);
原生方式 (Android M/API 23+):
kotlin
val color = context.getColor(R.color.colorPrimary)
过时方式 (Deprecated, 尽量避免):
java
// 在旧版本中常用,现在已弃用,因为它无法正确处理主题
int color = getResources().getColor(R.color.colorPrimary);
B. 解析 Hex 字符串
如果你从服务器获取了一个颜色代码字符串(如 "#FF0000"):
java
// Java/Kotlin
import android.graphics.Color;
try {
int color = Color.parseColor("#FF0000");
view.setBackgroundColor(color);
} catch (IllegalArgumentException e) {
// 处理格式错误
}
C. 使用 Color 类常量
Android Color 类提供了一些预定义的静态常量:
java
view.setBackgroundColor(Color.RED);
// 透明色
view.setBackgroundColor(Color.TRANSPARENT);
D. 动态构建颜色 (ARGB)
如果你需要动态计算颜色(例如根据滑动进度改变透明度):
java
// Java: Color.argb(alpha, red, green, blue) - 范围 0-255
int customColor = Color.argb(255, 100, 200, 50);
// Kotlin 扩展 (Android KTX)
val color = Color.valueOf(1.0f, 0.5f, 0.3f) // 范围 0.0-1.0 (API 26+)
E. 获取主题属性颜色 (Theme Attribute)
在代码中获取 ?attr/colorPrimary 这种动态颜色比较麻烦,需要解析主题:
java
// java
public static int getThemeColor(Context context, int attrId) {
TypedValue typedValue = new TypedValue();
Theme theme = context.getTheme();
// resolveAttribute 返回 true 表示找到了该属性
if (theme.resolveAttribute(attrId, typedValue, true)) {
return typedValue.data;
}
// 如果找不到,返回一个默认颜色(例如红色报错)
return Color.RED;
}
// 使用
// 获取 textColorPrimary (Android 系统自带属性)
int textPrimary = getThemeColor(this, android.R.attr.textColorPrimary);
// 设置给 View
textView.setTextColor(textPrimary );
kotlin
// Kotlin 扩展函数示例
fun Context.getThemeColor(attrId: Int): Int {
val typedValue = TypedValue()
theme.resolveAttribute(attrId, typedValue, true)
return typedValue.data
}
// 使用
val primaryColor = context.getThemeColor(com.google.android.material.R.attr.colorPrimary)
在 SettingsLib 中有工具类获取主题颜色值,可以参考:
java
//SettingsLib/src/com/android/settingslib/Utils.java
@ColorInt
public static int getColorAttrDefaultColor(Context context, int attr) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
@ColorInt int colorAccent = ta.getColor(0, 0);
ta.recycle();
return colorAccent;
}
public static ColorStateList getColorAttr(Context context, int attr) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
ColorStateList stateList = null;
try {
stateList = ta.getColorStateList(0);
} finally {
ta.recycle();
}
return stateList;
}
6. 颜色类型对照表
| 方式 | 语法/代码示例 | 适用场景 |
|---|---|---|
| Hex 码 | #FF0000, #80FFFFFF |
这里定义,UI设计稿对照 |
| XML 定义 | <color name="red">#FF0000</color> |
colors.xml 统一管理 |
| XML 引用 | @color/red |
布局文件常规使用 |
| 主题引用 | ?attr/colorPrimary |
推荐,支持深色模式和换肤 |
| 状态列表 | res/color/selector.xml |
按钮点击、文字禁用态 |
| 代码获取 | ContextCompat.getColor(...) |
逻辑中需要设置颜色 |
| 代码解析 | Color.parseColor("#...") |
处理服务端下发颜色 |
7. 主题中常用的属性颜色值和含义
在 styles.xml 或 themes.xml 中,以及布局引用(?attr/...)时,以下是最常用的语义化颜色属性。
A. 品牌/结构颜色 (Material Design)
| 属性名 (attr) | 含义 | 典型用途 |
|---|---|---|
colorPrimary |
主色调 | App 的品牌色,用于 Toolbar、按钮背景等。 |
colorSecondary |
次色调 | 强调色,用于 FAB 按钮、Switch 开关激活态、进度条等。 |
colorBackground |
窗口背景色 | Activity 的默认底色(浅色模式白,深色模式黑)。 |
colorSurface |
表面色 | CardView、BottomSheet、菜单的背景色。 |
B. 文本颜色 (Framework)
| 属性名 (android:attr) | 含义 | 典型用途 |
|---|---|---|
textColorPrimary |
主要文本色 | 标题、正文,最醒目的文字(深色模式下自动变浅)。 |
textColorSecondary |
次要文本色 | 副标题、描述性文字,透明度较低。 |
textColorHint |
提示文本色 | EditText 的 hint 颜色。 |
C. 控件交互颜色 (Control)
这是自定义控件时最需要关注的系统属性:
| 属性名 (attr) | 含义 | 典型用途 |
|---|---|---|
colorControlNormal |
默认状态色 | 图标默认颜色、CheckBox 未选中时的边框色、EditText 未聚焦时的下划线。 |
colorControlActivated |
激活状态色 | CheckBox 选中时的颜色、EditText 聚焦时的下划线/光标(通常等于 colorSecondary 或 colorPrimary)。 |
colorControlHighlight |
高亮/波纹色 | 按钮点击时的水波纹效果 (Ripple) 颜色。 |
8. ContextCompat 和 Context 的区别
- Context : 是 Android 系统核心抽象类(环境上下文)。随着 Android 版本升级(API 21, 23, 26...),
Context类的方法签名和行为发生了变化。直接使用Context的某些方法可能会导致在旧手机上崩溃。 - ContextCompat : 是 AndroidX (以前的 Support Library) 提供的一个帮助类(Helper Class)。它的作用是屏蔽系统版本的差异,提供统一的 API 接口。
A. 为什么要有 ContextCompat?
以获取颜色为例:
Android 6.0 (API 23) 之前: context.getResources().getColor(R.color.red)。
Android 6.0 (API 23) 及之后: Google 废弃了上面的方法,引入了 context.getColor(R.color.red)(为了更好地支持主题化)。
如果你直接写代码:
java
// 如果你在 API 21 的手机上运行这段代码(调用了 API 23 才有的方法),APP 会直接 Crash
context.getColor(R.color.red);
如果你使用 ContextCompat:
java
// ContextCompat 内部会自动判断当前手机系统版本
// if (SDK >= 23) call context.getColor()
// else call resources.getColor()
ContextCompat.getColor(context, R.color.red);
B. 使用场景总结
凡是涉及到获取资源(颜色、图片)或者系统权限 相关的操作,闭着眼睛使用 ContextCompat,它是最稳妥的方案。
| 场景 | 推荐使用 | 原因 |
|---|---|---|
| 获取资源 (Color, Drawable) | ContextCompat |
强烈推荐。防止 API 版本兼容性崩溃,且解决旧方法 Deprecated 警告。 |
| 检查权限 | ContextCompat |
checkSelfPermission 在旧版本处理方式不同,兼容库帮你处理了。 |
| 启动 Activity/Service | Context |
核心功能,直接用 Context 即可。 |
| 获取系统服务 | ContextCompat |
ContextCompat.getSystemService 某些情况下更安全,但通常直接用 Context 也没问题。 |