195

Consider this:

styles.xml

<style name="BlueTheme" parent="@android:style/Theme.Black.NoTitleBar">
    <item name="theme_color">@color/theme_color_blue</item>
</style>

attrs.xml

<attr name="theme_color" format="reference" />

color.xml

<color name="theme_color_blue">#ff0071d3</color>

So the theme color is referenced by the theme. How can I get the theme_color (reference) programmatically? Normally I would use getResources().getColor() but not in this case because it's referenced!

Seraphim's
  • 12,559
  • 20
  • 88
  • 129

11 Answers11

346

This should do the job:

TypedValue typedValue = new TypedValue();
Theme theme = context.getTheme();
theme.resolveAttribute(R.attr.theme_color, typedValue, true);
@ColorInt int color = typedValue.data;

Also make sure to apply the theme to your Activity before calling this code. Either use:

android:theme="@style/Theme.BlueTheme"

in your manifest or call (before you call setContentView(int)):

setTheme(R.style.Theme_BlueTheme)

in onCreate().

I've tested it with your values and it worked perfectly.

stkent
  • 19,772
  • 14
  • 85
  • 111
Emanuel Moecklin
  • 28,488
  • 11
  • 69
  • 85
  • thanks I can't try your solution yet cause I get an error: http://stackoverflow.com/questions/17278244/cant-use-referenced-color-by-theme-in-drawable Maybe you have experience in this... – Seraphim's Jun 24 '13 at 14:41
  • 6
    Anyway, with your solution I get a 0 value color (TypedValue{t=0x0/d=0x0})... I do not use declare-styleable, just a reference to the color – Seraphim's Jun 24 '13 at 15:10
  • Do you apply the theme to you activity? – Emanuel Moecklin Jun 24 '13 at 17:00
  • if theme_color is a reference to a ColorListState, how do you retrieve theme_color's "disabled" state color? – sudocoder Oct 21 '14 at 13:28
  • @Kimi sure it works, make sure that you have applied the theme – Emanuel Moecklin Feb 08 '16 at 03:24
  • No it's not. At least not for my case. And I'm sure I've applied the theme programmatically. Anyway, I turn to use another way to get values. – Kimi Chiu Feb 09 '16 at 05:12
  • Yes it does. If it doesn't work for you you're not doing it right. Post a question with code if you want a working solution. – Emanuel Moecklin Feb 09 '16 at 15:44
  • 5
    If you don't want to apply the theme to the activity, you can create a `ContextThemeWrapper` using the theme id and then retrieve the theme from that. – Ted Hopp May 19 '16 at 20:09
  • @TedHopp, how do you get the theme id? – Sakiboy Jan 11 '18 at 08:00
  • 1
    This method works in android X (material designing) – BlackBlind Sep 03 '19 at 08:32
  • 1
    This method doesn't work if `theme_color` references a `ColorStateList`: you can use [this solution](https://stackoverflow.com/a/49361848) to get default color or `AppCompatResources.getColorStateList(context, typedValue.resourceId)` to get `ColorStateList` object. – gmk57 Mar 09 '20 at 21:56
  • 13
    Now built in. The following is equivalent: `MaterialColors.getColor(context, R.attr.colorError, Color.GRAY)` – Mike Hanafey Aug 14 '20 at 23:26
101

We can use the utility class provided by Material Design library:

int color = MaterialColors.getColor(context, R.attr.theme_color, Color.BLACK)

NOTE: Color.BLACK is the default color in case the attribute supplied to the u

Chandra Sekhar
  • 18,914
  • 16
  • 84
  • 125
  • 9
    The best answer out of all – Ismail Iqbal Dec 31 '20 at 20:46
  • 4
    For sure using the MaterialColors utility class is the recommended way. Even is possible to get theme color base on a view: `val primaryColor = MaterialColors.getColor(view, R.attr.colorPrimary)` – android iPhone Feb 19 '21 at 11:17
  • 4
    This should be an accepted answer now in 2021 – the korovay Apr 08 '21 at 13:54
  • In my case "android.R.attr.textColorSecondary" for example is not working. Its just transparent. But the default color is not used, so i am stuck with this problem. Any help? – tzanke Feb 08 '22 at 10:46
  • @tzanke check your styles, mby you didnt declare `android:textColorSecondary`, not `textColorSecondary`. also make sure that the context had applied that style – Akbolat SSS Feb 08 '22 at 14:00
91

To add to the accepted answer, if you're using kotlin.

@ColorInt
fun Context.getColorFromAttr(
    @AttrRes attrColor: Int,
    typedValue: TypedValue = TypedValue(),
    resolveRefs: Boolean = true
): Int {
    theme.resolveAttribute(attrColor, typedValue, resolveRefs)
    return typedValue.data
}

and then in your activity you can do

textView.setTextColor(getColorFromAttr(R.attr.color))

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
Bri6ko
  • 1,858
  • 1
  • 18
  • 29
  • 2
    oook, thanks for the "integration". I'm not using kotlin but it's interesting. – Seraphim's Jul 27 '18 at 16:05
  • 12
    Well it makes TypedValue visible to the outside world. And for colors you always want to resolve referential declarations, so I have this: `@ColorInt fun Context.getThemeColor(@AttrRes attribute: Int) = TypedValue().let { theme.resolveAttribute(attribute, it, true); it.data }` (poorly formatted here but it's ok) – milosmns Feb 17 '19 at 13:09
  • 1
    Usage would be like this: `val errorColor = context.getThemeColor(R.attr.colorError)` – milosmns Feb 17 '19 at 13:11
  • 1
    More universal way, which also retrieves default value for a `ColorStateList`: `@ColorInt fun Context.getThemeColor(@AttrRes attribute: Int) = obtainStyledAttributes(intArrayOf(attribute)).use { it.getColor(0, Color.MAGENTA) }` (from [Nick Butcher](https://youtu.be/Owkf8DhAOSo?t=645)) – gmk57 Apr 11 '20 at 15:35
  • 1
    Ultimate way, which retrieves the whole `ColorStateList`, even if it references another theme attributes: `fun Context.getThemeColor(@AttrRes attribute: Int): ColorStateList = TypedValue().let { theme.resolveAttribute(attribute, it, true); AppCompatResources.getColorStateList(this, it.resourceId) }` (single colors will be wrapped in a `ColorStateList` too). – gmk57 Apr 11 '20 at 16:22
  • Where do you actually need to place the "Context.getColorFromAttr" function declaration in order for this snippet to work? – Tom Raganowicz Aug 21 '22 at 20:59
30

2021/January/8

If you want to get color from theme attributes, use the following steps.

Create a variable my_color and store the color from theme attributes as,

val my_color = MaterialColors.getColor(<VIEWOBJECT>, R.attr.<YOUATRRIBUTENAME>)

In place of <VIEWOBJECT>, pass a view object where you want to use the color, (behind the scenes it's just used to get the context as <VIEWOBJECT>.getContext() so that it can access resource i.e theme attribute values) . In place of <YOURATTRIBUTENAME>, use the name of the atrribute that you want to access

Example 1:

If you want want to get color referenced by theme attributes from certain activity. Create a variable that represents a view object on which you want to use the color. Here i have a textView in my activity, i'll just reference its object inside textview variable and pass it to the getColor method and behind the scenes it'll use that object to just get the context, so that it can access theme attribute values

val textview: TextView = findViewById(R.id.mytextview)
val my_color = MaterialColors.getColor(textView, R.attr<YOURATTRIBUTENAME>)

Example 2 :

If you want to get color from theme attributes inside a custom view then just use,

val my_color = MaterialColors.getColor(this, R.attr.<YOUATRRIBUTENAME>)

Inside a custom view this refers to the object of the custom view, which is in fact a view object.

Rishad Baniya
  • 665
  • 8
  • 14
25

This worked for me:

int[] attrs = {R.attr.my_attribute};
TypedArray ta = context.obtainStyledAttributes(attrs);
int color = ta.getResourceId(0, android.R.color.black);
ta.recycle();

if you want to get the hexstring out of it:

Integer.toHexString(color)
Sakiboy
  • 7,252
  • 7
  • 52
  • 69
Angel Solis
  • 511
  • 5
  • 20
14

Add this to your build.gradle (app):

implementation 'androidx.core:core-ktx:1.1.0'

And add this extension function somewhere in your code:

import androidx.core.content.res.use

@ColorInt
@SuppressLint("Recycle")
fun Context.themeColor(
    @AttrRes themeAttrId: Int
): Int {
    return obtainStyledAttributes(
        intArrayOf(themeAttrId)
    ).use {
        it.getColor(0, Color.MAGENTA)
    }
}
Andre Thiele
  • 3,202
  • 3
  • 20
  • 43
4

I use this kotlin extension

@ColorInt
fun Context.getColorFromAttr( @AttrRes attrColor: Int
): Int {
    val typedArray = theme.obtainStyledAttributes(intArrayOf(attrColor))
    val textColor = typedArray.getColor(0, 0)
    typedArray.recycle()
    return textColor
}

sample

getColorFromAttr(android.R.attr.textColorSecondary)
Codelaby
  • 2,604
  • 1
  • 25
  • 25
2

If you want to get multiple colors you can use:

int[] attrs = {R.attr.customAttr, android.R.attr.textColorSecondary, 
        android.R.attr.textColorPrimaryInverse};
Resources.Theme theme = context.getTheme();
TypedArray ta = theme.obtainStyledAttributes(attrs);

int[] colors = new int[attrs.length];
for (int i = 0; i < attrs.length; i++) {
    colors[i] = ta.getColor(i, 0);
}

ta.recycle();
Nicolas
  • 6,611
  • 3
  • 29
  • 73
1

For those who are looking for reference to a drawable you should use false in resolveRefs

theme.resolveAttribute(R.attr.some_drawable, typedValue, **false**);

lightning mcqueen
  • 163
  • 1
  • 1
  • 10
1

With me it only worked using ContextCompat and the typedValue.resourceId

As proposed in this question: How to get a value of color attribute programmatically

TypedValue typedValue = new TypedValue();
getTheme().resolveAttribute(R.attr.colorControlNormal, typedValue, true);
int color = ContextCompat.getColor(this, typedValue.resourceId)
Lucas Batista
  • 157
  • 2
  • 8
0

Here's a concise Java utility method that takes multiple attributes and return an array of color integers. :)

/**
 * @param context    Pass the activity context, not the application context
 * @param attrFields The attribute references to be resolved
 * @return int array of color values
 */
@ColorInt
static int[] getColorsFromAttrs(Context context, @AttrRes int... attrFields) {
    int length = attrFields.length;
    Resources.Theme theme = context.getTheme();
    TypedValue typedValue = new TypedValue();

    @ColorInt int[] colorValues = new int[length];

    for (int i = 0; i < length; ++i) {
        @AttrRes int attr = attrFields[i];
        theme.resolveAttribute(attr, typedValue, true);
        colorValues[i] = typedValue.data;
    }

    return colorValues;
}
varun
  • 2,027
  • 1
  • 15
  • 17
  • Java is better than Kotlin for this? – IgorGanapolsky Mar 15 '20 at 22:53
  • @IgorGanapolsky Oh, I honestly do not know. I shared my code since I knew it would come in handy to someone out there! I do not know Kotlin and I presume that Kotlin wouldn't make it perform any better, maybe fewer lines of code! :P – varun Mar 18 '20 at 20:13