ColorStateList 使用详解

ColorStateList 是 Android 中用于管理不同状态下颜色变化的工具,常用于按钮、文本、图标等 UI 组件。本文详细介绍 ColorStateList 的定义方式、使用方法以及高级用法。


1. ColorStateList 简介

ColorStateList 允许开发者为控件的不同状态指定不同的颜色。例如:

  • 按钮被按下时变成红色,默认状态下是绿色。
  • 文本在获取焦点时变为蓝色,失去焦点恢复默认颜色。
  • 自定义 Snackbar 背景颜色。

2. ColorStateList 的创建方式

2.1 在 XML 中定义 ColorStateList

可以在 res/color/ 目录下创建 .xml 文件来定义 ColorStateList,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下状态(高优先级) -->
<item android:state_pressed="true" android:color="#FF0000" />

<!-- 选中状态 -->
<item android:state_checked="true" android:color="#00FF00" />

<!-- 禁用状态 -->
<item android:state_enabled="false" android:color="#808080" />

<!-- 默认状态(必须放最后,否则可能被前面的状态匹配覆盖) -->
<item android:color="#000000" />
</selector>

说明:

  • state_pressed="true":控件被按下时,颜色变为 #FF0000(红色)。
  • state_checked="true":控件被选中时,颜色变为 #00FF00(绿色)。
  • state_enabled="false":控件被禁用时,颜色变为 #808080(灰色)。
  • 默认颜色必须放在最后,否则可能会被前面状态覆盖。

2.2 代码动态创建 ColorStateList

如果需要在运行时动态生成 ColorStateList,可以使用 ColorStateList() 构造方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
val states = arrayOf(
intArrayOf(android.R.attr.state_pressed), // 按下状态
intArrayOf(android.R.attr.state_checked), // 选中状态
intArrayOf(-android.R.attr.state_enabled), // 禁用状态
intArrayOf() // 默认状态
)

val colors = intArrayOf(
Color.RED, // 按下时红色
Color.GREEN, // 选中时绿色
Color.GRAY, // 禁用时灰色
Color.BLACK // 默认状态黑色
)

// 创建 ColorStateList 对象
val colorStateList = ColorStateList(states, colors)

参数解析:

  • states:定义不同状态的数组,使用 intArrayOf 来表示不同的状态。
  • colors:与 states 一一对应的颜色数组。

3. ColorStateList 的应用场景

3.1 设置文本颜色

TextView 及其子类(如 Button)可以使用 setTextColor() 直接应用 ColorStateList

1
textView.setTextColor(colorStateList)

如果在 XML 中定义了 ColorStateList(比如 res/color/text_color.xml),可以直接在 XML 中使用:

1
2
3
4
5
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World"
android:textColor="@color/text_color"/>

或者在代码中加载:

1
2
val textColor = ContextCompat.getColorStateList(context, R.color.text_color)
textView.setTextColor(textColor)

3.2 设置背景颜色(TintList)

有些控件(如 FloatingActionButtonButton)支持 backgroundTintList,可用于改变背景颜色:

1
2
fab.backgroundTintList = colorStateList
button.backgroundTintList = colorStateList

在 XML 中可以使用:

1
2
3
4
5
xml复制编辑<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me"
android:backgroundTint="@color/button_color"/>

3.3 设置图标颜色(ImageView & Drawable)

对于 ImageView,可以使用 setImageTintList() 设置 Tint 颜色:

1
imageView.imageTintList = colorStateList

对于 Drawable,可以使用 setTintList()

1
2
3
val drawable = ContextCompat.getDrawable(context, R.drawable.ic_example)
drawable?.setTintList(colorStateList)
imageView.setImageDrawable(drawable)

3.4 设置 Snackbar 背景颜色

Snackbar 中,我们可以使用 backgroundTintList 来改变背景颜色:

1
2
3
4
5
6
7
8
9
10
val snackbar = Snackbar.make(view, "Hello Snackbar", Snackbar.LENGTH_SHORT)

// 修改背景颜色
snackbar.view.backgroundTintList = colorStateList

// 修改文字颜色
val textView = snackbar.view.findViewById<TextView>(com.google.android.material.R.id.snackbar_text)
textView.setTextColor(Color.BLACK)

snackbar.show()

3.5 设置 PopupMenu 颜色

PopupMenu 默认颜色受 Material Design 主题影响,可以用 ColorStateList 修改 MenuItem 颜色:

1
2
3
4
5
6
7
8
9
10
11
12
val popupMenu = PopupMenu(this, view)
popupMenu.menuInflater.inflate(R.menu.popup_menu, popupMenu.menu)

// 获取 PopupMenu 的 MenuItem 并设置 TextColor
for (i in 0 until popupMenu.menu.size()) {
val item = popupMenu.menu.getItem(i)
val spannable = SpannableString(item.title)
spannable.setSpan(ForegroundColorSpan(Color.RED), 0, spannable.length, 0)
item.title = spannable
}

popupMenu.show()

4. 高级用法

4.1 使用 withAlpha() 改变透明度

ColorStateList.valueOf(Color.RED).withAlpha(128) 可以为颜色增加透明度:

1
2
val colorStateList = ColorStateList.valueOf(Color.RED).withAlpha(128)
textView.setTextColor(colorStateList)

4.2 结合 Material Theme 适配暗黑模式

res/color/ 目录下创建 color.xml

1
2
3
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorPrimary" />
</selector>

这样 ColorStateList 会自动适配 Material Theme 颜色。


5. 总结

  1. ColorStateList 允许为控件的不同状态指定不同颜色,支持 XML 和代码创建。
  2. 常用于 TextViewButtonImageViewSnackbarPopupMenu 等控件的颜色控制。
  3. 结合 TintList 可以改变背景、图标、Drawable 颜色。
  4. 通过 withAlpha() 可以调整透明度,结合 Material Theme 可适配暗黑模式。

掌握 ColorStateListTintList,可以让 UI 颜色控制更加灵活!

我的实践 实现一个按钮按下时变成红色,默认状态下是绿色

要实现“按钮被按下时变成红色,默认状态下是绿色”,可以使用 ColorStateList 结合 backgroundTintListtextColor。下面是具体的 XML 和 Kotlin 代码示例。


方式 1:使用 XML 定义 ColorStateList

1.1 创建按钮颜色状态列表

res/color/ 目录下创建一个 button_color.xml 文件:

1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 按下状态(高优先级) -->
<item android:state_pressed="true" android:color="#FF0000" /> <!-- 红色 -->

<!-- 默认状态(最后定义) -->
<item android:color="#00FF00" /> <!-- 绿色 -->
</selector>

1.2 在 XML 布局中应用

activity_main.xml 中:

1
2
3
4
5
6
7
<Button
android:id="@+id/myButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Click Me"
android:textColor="@android:color/white"
android:backgroundTint="@color/button_color"/>

解释:

  • 这个 Button 的背景颜色由 button_color.xml 控制。
  • state_pressed="true"(即按钮被按下时),背景变为 #FF0000(红色)。
  • 其他情况下,背景保持 #00FF00(绿色)。

方式 2:在代码中动态创建 ColorStateList

如果不想使用 XML,可以直接在 Kotlin 代码中动态创建 ColorStateList

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.widget.Button
import androidx.appcompat.app.AppCompatActivity

class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val myButton: Button = findViewById(R.id.myButton)

// 定义按钮不同状态的颜色
val states = arrayOf(
intArrayOf(android.R.attr.state_pressed), // 按下状态
intArrayOf() // 默认状态
)

val colors = intArrayOf(
Color.RED, // 按下时红色
Color.GREEN // 默认状态绿色
)

// 创建 ColorStateList 并应用到按钮背景
val colorStateList = ColorStateList(states, colors)
myButton.backgroundTintList = colorStateList
}
}

解释:

  • states 定义了两种状态:
    • android.R.attr.state_pressed(按钮被按下)。
    • intArrayOf() 代表默认状态。
  • colors 定义了对应状态的颜色:
    • 按下时(state_pressed)是红色 (Color.RED)。
    • 默认状态是绿色 (Color.GREEN)。
  • myButton.backgroundTintList = colorStateList 应用 ColorStateList

效果

  1. 按钮默认情况下是绿色。
  2. 当用户按下按钮时,按钮变为红色。
  3. 松开后恢复为绿色。

这样,你就成功让按钮按下变红,松开变绿了!🚀