Obtenga el valor del color mediante programación cuando sea una referencia (tema)

116

Considera esto:

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>

Por tanto, el tema hace referencia al color del tema. ¿Cómo puedo obtener el theme_color (referencia) mediante programación? Normalmente lo usaría, getResources().getColor()pero no en este caso porque se hace referencia.

Serafines
fuente

Respuestas:

255

Esto debería hacer el trabajo:

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

También asegúrese de aplicar el tema a su actividad antes de llamar a este código. O use:

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

en su manifiesto o llamada (antes de llamar setContentView(int)):

setTheme(R.style.Theme_BlueTheme)

en onCreate().

Lo probé con tus valores y funcionó perfectamente.

Emanuel Moecklin
fuente
gracias No puedo probar tu solución todavía porque me sale un error: stackoverflow.com/questions/17278244/… Tal vez tengas experiencia en esto ...
Seraphim's
5
De todos modos, con su solución obtengo un color de valor 0 (TypedValue {t = 0x0 / d = 0x0}) ... No uso declare-styleable, solo una referencia al color
Seraphim's
¿Aplicas el tema a tu actividad?
Emanuel Moecklin
5
Si no desea aplicar el tema a la actividad, puede crear un ContextThemeWrapperusando la identificación del tema y luego recuperar el tema de allí.
Ted Hopp
1
Este método funciona en Android X (diseño de materiales)
BlackBlind
43

Para agregar a la respuesta aceptada, si está usando kotlin.

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

y luego en tu actividad puedes hacer

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

Bri6ko
fuente
2
oook, gracias por la "integración". No estoy usando kotlin pero es interesante.
Seraphim's
5
Bueno, hace que TypedValue sea visible para el mundo exterior. Y para los colores, siempre desea resolver declaraciones de referencia, así que tengo esto: @ColorInt fun Context.getThemeColor(@AttrRes attribute: Int) = TypedValue().let { theme.resolveAttribute(attribute, it, true); it.data }(mal formateado aquí pero está bien)
milosmns
1
El uso sería así:val errorColor = context.getThemeColor(R.attr.colorError)
milosmns
Una forma más universal, que también recupera el valor predeterminado para a ColorStateList: @ColorInt fun Context.getThemeColor(@AttrRes attribute: Int) = obtainStyledAttributes(intArrayOf(attribute)).use { it.getColor(0, Color.MAGENTA) }(de Nick Butcher )
gmk57
Ultimate way, que recupera el todo ColorStateList, incluso si hace referencia a los atributos de otro tema: fun Context.getThemeColor(@AttrRes attribute: Int): ColorStateList = TypedValue().let { theme.resolveAttribute(attribute, it, true); AppCompatResources.getColorStateList(this, it.resourceId) }(los colores individuales también se incluirán ColorStateList).
gmk57
24

Esto funcionó para mí:

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

si quieres sacar la cadena hexadecimal:

Integer.toHexString(color)
Ángel solis
fuente
Esto debería devolver un ColorRes, no un ColorInt.
Miha_x64
Terminé usando esto con getColorResource (color) y no llamé a reciclar.
Zeek Aran
2

Si desea obtener varios colores, puede 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
fuente
2

Agregue esto a su build.gradle (aplicación):

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

Y agregue esta función de extensión en algún lugar de su código:

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

Aquí hay un método de utilidad de Java conciso que toma múltiples atributos y devuelve una matriz de enteros de color. :)

/**
 * @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
fuente
¿Java es mejor que Kotlin para esto?
IgorGanapolsky
@IgorGanapolsky Oh, honestamente no lo sé. ¡Compartí mi código porque sabía que sería útil para alguien! No conozco a Kotlin y supongo que Kotlin no lo haría funcionar mejor, ¡tal vez menos líneas de código! : P
varun
-1

Para aquellos que buscan una referencia a un dibujable, debe usar falseenresolveRefs

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

Kilo
fuente
¿A qué se refiere la variable typedValue?
BENN1TH
¿A qué se refiere el tema variable. *?
BENN1TH