您的位置:首页 > 移动开发 > Android开发

分析Android多主题颜色的相关问题

2016-08-16 10:06 567 查看

如果您通过以下的代码来获取定义的颜色值

context.getResources().getColor(R.color.some_color_resource_id);

在 Android Studio 中会有一个 lint 警告,提示您

Resources#getColor(int)
Marshmallow
中被废弃了,建议使用主题可知的
Resources#getColor(int, Theme
) 函数。 为了避免该警告,则可以使用
ContextCompat

ContextCompat.getColor(context, R.color.some_color_resource_id);

该函数的实现是这样的:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
return context.getResources().getColor(id, context.getTheme());
} else {
return context.getResources().getColor(id);
}

看起来很简单。但是为什么会这样呢? 为什么会开始使用带主题的函数而废弃之前的函数呢?

Resources#getColor(int)&Resources#getColorStateList(int)的问题

首先来看看这两个被废弃的函数是干啥的:
      –

Resources#getColor(int)
返回一个资源 id 对应的颜色值,如果该资源为
ColorStateList
则返回
ColorStateList
的默认颜色值

      –

Resources#getColorStateList(int)
返回对应的
ColorStateList

上面的代码在什么情况下会破坏我的代码呢?

要理解为何废弃这两个函数,来看个 ColorStateList的例子。 当在 TextView 中使用自定义的 ColorStateList的时候, TextView 不可用状态和可用状态的文字颜色分别使用

R.attr.colorAccent
R.attr.colorPrimary
表示。

XHTML

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorAccent" android:state_enabled="false"/>
<item android:color="?attr/colorPrimary"/>
</selector>

现在如果您通过如下的代码来获取这个

ColorStateList

ColorStateListcsl = context.getResources().getColorStateList(R.color.button_text_csl);

上面的代码会抛出一个异常(查看logcat 可以看到如下的信息)

W/Resources: ColorStateListcolor/button_text_csl has unresolved theme attributes!
Consider using Resources.getColorStateList(int, Theme)
or Context.getColorStateList(int)
at android.content.res.Resources.getColorStateList(Resources.java:1011)
...

哪里出错了呢?

问题的根源在于

Resources
对象并没有和一个
Theme
对象关联,当使用
R.attr.colorAccent
 R.attr.colorPrimary
指代颜色的时候,在代码中通过上面的函数解析的时候没有指定对应的 Theme导致无法解析出结果。 所以在
Marshmallow
中添加了
ColorStateList
对 Theme的支持并且添加了这两个新的函数:
Resources#getColor(int, Theme)
Resources#getColorStateList(int, Theme),
并使用 Theme参数来解析里面的
attributes
属性。

在新版本的 Support 库中也有对应的实现,分别位于

ResourcesCompat
ContextCompat
类中。

如何解决该问题呢?

使用 AppCompat v24+ 版本可以很容易的解决该问题。

ColorStateListcsl = AppCompatResources.getColorStateList(context, R.color.button_text_csl);

在 23+ 版本上直接使用系统的函数,在之前的版本上 AppCompat 自己解析这些 xml 文件从里面提取 attr 属性指代的数值。 AppCompat 同时还支持 ColorStateList新的

android:alpha
属性。

Resources#getDrawable(int)的问题

Resources#getDrawable(int)
和前面的两个函数的问题是类似的。 在 Lollipop 之前的版本中无法支持 Themeattr 。

为啥我这样用也没有出现异常呢?

异常并不总是会出现。

VectorDrawableCompat
AnimatedVectorDrawableCompat
类中添加了和
AppCompatResources
类类似的功能。比如在 矢量图中你可以使用
?attr/colorControlNormal
来设置矢量图的颜色,
VectorDrawableCompat
会自动完成解析该 属性的工作:

XHTML

<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0"
android:tint="?attr/colorControlNormal">
<path
android:pathData="..."
android:fillColor="@android:color/white"/>
</vector>

小测试

下面使用一个小测试来回顾一下前面介绍的内容。 假设有下面一个 ColorStateList:

XHTML

<!-- res/colors/button_text_csl.xml --><selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?attr/colorAccent" android:state_enabled="false"/>
<item android:color="?attr/colorPrimary"/>
</selector>

在应用中定义了如下的 Theme:

XHTML

<!-- res/values/themes.xml -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">@color/vanillared500</item>
<item name="colorPrimaryDark">@color/vanillared700</item>
<item name="colorAccent">@color/googgreen500</item>
</style>
<style name="CustomButtonTheme" parent="ThemeOverlay.AppCompat.Light">
<item name="colorPrimary">@color/brown500</item>
<item name="colorAccent">@color/yellow900</item>
</style>

在代码中有如下的函数用来解析颜色值并在代码中创建 ColorStateList:

@ColorInt
private static int getThemeAttrColor(Context context, @AttrRes int colorAttr) {
TypedArray array = context.obtainStyledAttributes(null, new int[]{colorAttr});
try {
return array.getColor(0, 0);
} finally {
array.recycle();
}
}
private static ColorStateListcreateColorStateList(Context context) {
return new ColorStateList(
new int[][]{
new int[]{-android.R.attr.state_enabled}, // Disabled state.
StateSet.WILD_CARD,            // Enabled state.
},
new int[]{
getThemeAttrColor(context, R.attr.colorAccent), // Disabled state.
getThemeAttrColor(context, R.attr.colorPrimary), // Enabled state.
});
}

 看看是否能猜出在 API 19 和 API 23 版本上文字禁用状态和正常状态的颜色,实现代码如下(5和8的情况,在TextView xml 中指定了

android:theme=”@style/CustomButtonTheme”
):

Resourcesres = ctx.getResources();
// (1)
int deprecatedTextColor = res.getColor(R.color.button_text_csl);
button1.setTextColor(deprecatedTextColor);
// (2)
ColorStateListdeprecatedTextCsl = res.getColorStateList(R.color.button_text_csl);
button2.setTextColor(deprecatedTextCsl);
// (3)
int textColorXml =
AppCompatResources.getColorStateList(ctx, R.color.button_text_csl).getDefaultColor();
button3.setTextColor(textColorXml);
// (4)
ColorStateListtextCslXml = AppCompatResources.getColorStateList(ctx, R.color.button_text_csl);
button4.setTextColor(textCslXml);
// (5)
Context themedCtx = button5.getContext();
ColorStateListtextCslXmlWithCustomTheme=
AppCompatResources.getColorStateList(themedCtx, R.color.button_text_csl);
button5.setTextColor(textCslXmlWithCustomTheme);
// (6)
int textColorJava = getThemeAttrColor(ctx, R.attr.colorPrimary);
button6.setTextColor(textColorJava);
// (7)
ColorStateListtextCslJava = createColorStateList(ctx);
button7.setTextColor(textCslJava);
// (8)
Context themedCtx = button8.getContext();
ColorStateListtextCslJavaWithCustomTheme= createColorStateList(themedCtx);
button8.setTextColor(textCslJavaWithCustomTheme);

下面是对应的实现截图:

 

总结 

以上就是关于分析Android多主题颜色的相关问题的全部内容,希望本文的内容对大家开发Android能有所帮助。

您可能感兴趣的文章:

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 主题 颜色