Obtenha o valor da cor programaticamente quando for uma referência (tema)

116

Considere isto:

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>

Portanto, a cor do tema é referenciada pelo tema. Como posso obter o theme_color (referência) programaticamente? Normalmente eu usaria, getResources().getColor()mas não neste caso porque é referenciado!

Serafim
fonte

Respostas:

254

Isso deve fazer o trabalho:

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

Além disso, certifique-se de aplicar o tema à sua atividade antes de chamar este código. Use:

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

no seu manifesto ou chamada (antes de ligar setContentView(int)):

setTheme(R.style.Theme_BlueTheme)

no onCreate().

Eu testei com seus valores e funcionou perfeitamente.

Emanuel Moecklin
fonte
obrigado. Não posso tentar sua solução ainda porque recebo um erro: stackoverflow.com/questions/17278244/… Talvez você tenha experiência nisso ...
Serafim de
5
De qualquer forma, com sua solução eu obtenho um valor de cor 0 (TypedValue {t = 0x0 / d = 0x0}) ... Eu não uso declarar estilizável, apenas uma referência à cor
Serafim
Você aplica o tema à sua atividade?
Emanuel Moecklin
5
Se não quiser aplicar o tema à atividade, você pode criar um ContextThemeWrapperusando o id do tema e, em seguida, recuperar o tema a partir dele.
Ted Hopp de
1
Este método funciona no android X (design de material)
BlackBlind
43

Para adicionar à resposta aceita, se você estiver usando kotlin.

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

e então em sua atividade você pode fazer

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

Bri6ko
fonte
2
oook, obrigado pela "integração". Não estou usando o kotlin, mas é interessante.
Serafim de
5
Bem, isso torna TypedValue visível para o mundo exterior. E para cores você sempre quer resolver declarações referenciais, então eu tenho isto: @ColorInt fun Context.getThemeColor(@AttrRes attribute: Int) = TypedValue().let { theme.resolveAttribute(attribute, it, true); it.data }(mal formatado aqui, mas está ok)
milosmns
1
O uso seria assim:val errorColor = context.getThemeColor(R.attr.colorError)
milosmns
ColorStateList@ColorInt fun Context.getThemeColor(@AttrRes attribute: Int) = obtainStyledAttributes(intArrayOf(attribute)).use { it.getColor(0, Color.MAGENTA) }
Maneira
Forma final, que recupera o todo ColorStateList, mesmo que faça referência a outros atributos do tema: fun Context.getThemeColor(@AttrRes attribute: Int): ColorStateList = TypedValue().let { theme.resolveAttribute(attribute, it, true); AppCompatResources.getColorStateList(this, it.resourceId) }(cores únicas também serão envolvidas em um ColorStateList).
gmk57
24

Isso funcionou para mim:

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

se você quiser tirar o hexstring dele:

Integer.toHexString(color)
Angel Solis
fonte
Isso deve retornar um ColorRes, não um ColorInt.
Miha_x64
Acabei usando isso com getColorResource (color) e não chamei recycle.
Zeek Aran
2

Se você deseja obter várias cores, pode usar:

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
fonte
2

Adicione isso ao seu build.gradle (app):

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

E adicione esta função de extensão em algum lugar do seu código:

@ColorInt
@SuppressLint("Recycle")
fun Context.themeColor(
    @AttrRes themeAttrId: Int
): Int {
    return obtainStyledAttributes(
        intArrayOf(themeAttrId)
    ).use {
        it.getColor(0, Color.MAGENTA)
    }
}
André Ramon
fonte
0

Aqui está um método utilitário Java conciso que usa vários atributos e retorna uma matriz de inteiros coloridos. :)

/**
 * @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
fonte
Java é melhor que Kotlin para isso?
IgorGanapolsky
@IgorGanapolsky Oh, honestamente não sei. Compartilhei meu código porque sabia que seria útil para alguém lá fora! Não conheço Kotlin e presumo que Kotlin não faria com que ele tivesse um desempenho melhor, talvez menos linhas de código! : P
varun
-1

Para aqueles que procuram uma referência para um drawable, você deve usar falseemresolveRefs

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

Quilo
fonte
Qual é a variável typedValue em referência?
BENN1TH
Qual é o tema da variável. * Em referência?
BENN1TH