Issue
Background
Starting from API 23, the small method of getting a color getColor (by just giving it the resource id) of the resources is deprecated:
Instead, we are told to use a method that includes a Theme
parameter too:
This method was deprecated in API level 23. Use getColor(int, Theme) instead.
https://developer.android.com/reference/android/content/res/Resources.html#getColor(int, android.content.res.Resources.Theme)
The problem
The docs don't say much about the theme
:
https://developer.android.com/reference/android/content/res/Resources.html#getColor(int, android.content.res.Resources.Theme)
What I've found
Searching over the Internet, all I could find is that we can use the support library to get the color :
ContextCompat.getColor(context, R.color.color_name);
This is a bit weird, because under the hood, it doesn't seem like it's doing anything with themes. Here's its code:
@ColorInt
public static final int getColor(@NonNull Context context, @ColorRes int id) {
if (Build.VERSION.SDK_INT >= 23) {
return context.getColor(id);
} else {
return context.getResources().getColor(id);
}
}
Looking at Context.getColor, I can see this:
So it seems to me it's using the theme of the activity?
The questions
What's the purpose of the 'theme' parameter?
How is it used? Is there any sample/tutorial/article about it?
Why was the function deprecated anyway? It still seems safe to use for me...
What's the use of the support library function? How different is it from using the deprecated function?
Solution
tl;dr
- If your min SDK is 23 you don't need the compat API for obtaining colors.
- If you don't use theme attribute references in colors and don't plan change, use
Context.getResources().getColor(int)
orgetColorStateList(int)
as you're used to. It's marked deprecated but functionally it's OK. - If you want to use theme attribute references in colors on API 23+ only, use
ContextCompat.getColorStateList(Context, int)
. - If you use AppCompat and want to be able to use theme attribute references on all versions of Android, use
AppCompatResources.getColorStateList(Context, int)
.
Starting API 23 ColorStateList
can contain theme attribute references, the same thing that's allowed in Drawable
since API 21.
Theme attributes are mostly used in color state lists obtained by getColorStateList
method as opposed to getColor
so I'll use that from now on. Most of what's mentioned applies to both variants.
Example:
res/color/my_csl.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="?attr/colorControlHighlight" android:alpha="?android:disabledAlpha"/>
<item android:color="?attr/colorControlHighlight"/>
</selector>
What's the purpose of the 'theme' parameter?
Theme is required to resolve theme attributes. Resources itself is not aware of any theme. Theme is provided by a context.
context.getColorStateList(R.color.my_csl);
is essentially a shortcut to
context.getResources().getColorStateList(R.color.my_csl, context.getTheme());
Both of these methods are part of public API so both of them were backported:
ContextCompat.getColorStateList(context, R.color.my_csl);
ResourcesCompat.getColorStateList(context.getResources(), R.color.my_csl, context.getTheme());
How is it used?
Typically it's as simple as this:
final int myColor = ContextCompat.getColorStateList(context, R.color.my_csl);
If you use AppCompat you might be better off with another option:
final int myColor = AppCompatResources.getColorStateList(context, R.color.my_csl);
Here's a comparison between these two options:
ContextCompat.getColorStateList
- Requires only
support-compat
support library - Backports only API - it will crash when trying to resolve theme attributes below API 23
- Requires only
AppCompatResources.getColorStateList
- Requires full
appcompat-v7
support library - Backports functionality - it will respect context theme even below API 23
- Requires full
ResourcesCompat.getColorStateList(int, Theme)
is API (not functional) backport of Resources.getColorStateList(int, Theme)
which is used internally by Context.getColorStateList(int)
on API 23+. In everyday use you won't be needing this method.
Why was the function deprecated anyway? It still seems safe to use for me...
The method was deprecated to migrate developers from the version that's not aware of themes to the theme-aware version of the method.
The theme-unaware method is still perfectly safe to use as long as you use it to obtain colors without theme attribute references, for example:
res/values/colors.xml
<resources>
<color name="my_cint">#f00</color>
</resources>
can be safely obtained by
context.getResources().getColor(R.color.my_cint);
What's the use of the support library function?
ContextCompat.getColorStateList(context, R.color.my_csl);
is literally this
public static final ColorStateList getColorStateList(Context context, @ColorRes int id) {
if (Build.VERSION.SDK_INT >= 23) {
return context.getColorStateList(id);
} else {
return context.getResources().getColorStateList(id);
}
}
The method exists so you don't have to write if-else everywhere. That's it.
On API 23+ it can use the context theme. Below API 23 it falls back to the old version that cannot resolve theme attributes.
ResourcesCompat.getColorStateList(int, Theme)
looks and works similarly, it ignores theme attributes (crashes when you use them) below API 23.
AppCompatResources.getColorStateList(Context, int)
will not crash and correctly resolve theme attributes on all versions of Android.
What about
ResourcesCompat#getColorStateList
?
You won't typically need this method.
It's a layer of abstraction. It's telling you that to resolve a color you don't need any god-object, any Context
. Resources
is just enough to resolve a color. BUT for Resources
to be able to resolve theme attributes you need to provide a Theme
. The fact that a Theme
can be obtained from a Context
is of no concern to Resources
. Resources
doesn't disclose or care that it itself came from some Context.getResources()
.
Can you [...] update about
getColor
too?
getColor
will resolve
<color>
resource as color integer<selector>
color state list resource as color integer of its default color
getColorStateList
will resolve
<color>
resource asColorStateList
with one color<selector>
CSL resource asColorStateList
with specified colors
Use the one that's required by consumer API.
There is no AppCompatResources.getColor(Context, int)
because there's little point in a color resource being defined as a theme attribute reference. If you ever need this AppCompatResources.getColorStateList(Context, int).getDefaultColor()
is the functional equivalent.
ContextCompat.getColor(Context, int)
and ResourcesCompat.getColor(Resources, int, Theme)
exist because they mirror the framework API. Their practical usefulness, as described above, is debatable.
I recommend reading the discussion below to grasp some of the elusive subtle differences.
Answered By - Eugen Pechanec
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.