Obtaining themed attributes from built in Android styles - android

Given a Context that has been themed with AppTheme (shown below), is it possible to programmatically obtain the color #ff11cc00 without referencing R.style.MyButtonStyle, R.style.AppTheme, or android.R.style.Theme_Light?
The objective is to obtain the button text color that was set by the theme without being bound to a particular theme or app-declared resource. Using android.R.style.Widget_Button and android.R.attr.textColor is okay.
<style name="AppTheme" parent="Theme.Light">
<item name="android:buttonStyle">#style/MyButtonStyle</item>
</style>
<style name="MyButtonStyle" parent="android:style/Widget.Button">
<item name="android:textColor">#ff11cc00</item>
</style>

Try the following:
TypedValue outValue = new TypedValue();
Theme theme = context.getTheme();
theme.resolveAttribute(android.R.attr.buttonStyle, outValue , true);
int[] attributes = new int[1];
attributes[0] = android.R.attr.textColor;
TypedArray styledAttributes = theme.obtainStyledAttributes(outValue.resourceId, attributes);
int color = styledAttributes.getColor(0, 0);

I can think of this round about way, may be someone else will know better:
int theme ;
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD) {
theme = android.R.style.Theme;
} else {
theme = android.R.style.Theme_DeviceDefault;
}
ContextThemeWrapper wrapper = new ContextThemeWrapper(context, theme);
Button button = new Button(wrapper);
ColorStateList colorStateList = button.getTextColors();
colorStateList.getColorForState(button.getDrawableState(), R.color.default_color);

Related

How to get color from theme (not current)?

I have some themes in styles, that can be select. And setTheme(getThemeResId()) in BaseActivity. Everywhere I need I get the main color by ?attr/colorPrimary. But in settings, I want to show all themes colors. Smth like getColor(R.attr.colorPrimary, R.style.AppTheme1). How to do this?
int[] attrs = { R.attr.colorPrimary, R.attr.colorAccent, R.attr.colorSecondary };
TypedArray ta = getContext().obtainStyledAttributes(R.style.AppTheme1, attrs);
int colorPrimary = ta.getColor(0, Color.BLACK);
int colorAccent = ta.getColor(1, Color.BLACK);
int colorSecondary = ta.getColor(2, Color.BLACK);
Log.i("LOG", "colorPrimary as hex:" + Integer.toHexString(colorPrimary));

how to dynamically change color using the defined attribute

Having two theme, it can be dynamically switched.
There is a txtColor attribute defined in
attrs.xml
<attr name=“txtColor” format="reference" />
in themes.xml, defined the color for the attribute in different theme
<style name=“CustomLight" parent="AppTheme.Base">
<item name="txtColor”>#000000</item>
<style name=“CustomDark" parent="AppTheme.Base">
<item name="txtColor”>#ffffff</item>
in layout file, using the attribute is fine
android:textColor="?attr/txtColor"
but got exception when try to use the txtColor attribute
Caused by: android.content.res.Resources$NotFoundException: Resource ID #0x7f010015
txtView.setTextColor(getResources().getColor(R.attr.txtColor));
question: how to change the color dynamically using the attribute?
First the attribute format should be "color"
<attr name="txtColor" format="color"/>
Then you can set the color doing this:
int[] attrs = {R.attr.txtColor} ;
try { //getPackageManager() can throw an exeption
Activity activity = getActivity();
themeId = activity.getPackageManager().getActivityInfo(activity.getComponentName(), 0).theme;
TypedArray ta = activity.obtainStyledAttributes(themeId, attrs);
int color = ta.getColor(0, Color.BLACK); //I set Black as the default color
txtView.setTextColor(color);
ta.recycle();
} catch (NameNotFoundException e) {
e.printStackTrace();
}
I think I found a simpler solution which worked with the existing attrs, here it in case someone is looking for the same, any simpler ones? Thanks!
public static int getColorByThemeAttr(Context context, int attr, int defaultColor) {
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
boolean got = theme.resolveAttribute(attr, typedValue, true);
return got ? typedValue.data : defaultColor;
}

Android actionbarsherlock get height return 0

I was looking for get the height of my actionbar(sherlock). The value returned is 0.
value/styles.xml (and for value11 I used "android:actionBarSize")
<style name="AppTheme" parent="Theme.Sherlock.Light.DarkActionBar">
<item name="actionBarStyle">#style/Widget.AppTheme.ActionBar</item>
<item name="actionBarSize">48dip</item>
</style>
code :
getSupportActionBar().getHeight()
This didn't work?
// Calculate ActionBar height
TypedValue tv = new TypedValue();
if (getTheme().resolveAttribute(android.R.attr.actionBarSize, tv, true))
{
actionBarHeight = TypedValue.complexToDimensionPixelSize(tv.data,getResources().getDisplayMetrics());
}
ref: How to get the ActionBar height?

Get color value programmatically when it's a reference (theme)

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!
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.
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))
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
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.
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)
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)
}
}
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)
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();
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;
}
For those who are looking for reference to a drawable you should use false in resolveRefs
theme.resolveAttribute(R.attr.some_drawable, typedValue, **false**);
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)

How to retrieve drawable from attributes reference

I defined theme and style inside my app. icons (drawable) are defined using reference in style file as
<attr name="myicon" format="reference" />
and style as
<style name="CustomTheme" parent="android:Theme.Holo">
<item name="myicon">#drawable/ajout_produit_light</item>
I need to retrieve the drawable programmatically to use the good image in a dialogfragment.
If I make like
mydialog.setIcon(R.style.myicon);
I get an id equals to 0, so no image
I tried to use something like
int[] attrs = new int[] { R.drawable.myicon};
TypedArray ta = getActivity().getApplication().getTheme().obtainStyledAttributes(attrs);
Drawable mydrawable = ta.getDrawable(0);
mTxtTitre.setCompoundDrawables(mydrawable, null, null, null);
I tried different things like that but result is always 0 or null :-/
How I can I do this ?
I found the solution on
Access resource defined in theme and attrs.xml android
TypedArray a = getTheme().obtainStyledAttributes(R.style.AppTheme, new int[] {R.attr.homeIcon});
int attributeResourceId = a.getResourceId(0, 0);
Drawable drawable = getResources().getDrawable(attributeResourceId);
Kotlin solution:
val typedValue = TypedValue()
context.theme.resolveAttribute(R.attr.yourAttr, typedValue, true)
val imageResId = typedValue.resourceId
val drawable = ContextCompat.getDrawable(context, imageResId) ?: throw IllegalArgumentException("Cannot load drawable $imageResId")
With the assumption your context (activity) is themed the way you want it, you can use resolveAttribute on the theme:
TypedValue themedValue = new TypedValue();
this.getTheme().resolveAttribute(R.attr.your_attribute, themedValue, true);
myView.setBackgroundResource(themedValue.resourceId);
So in your case it would look something like this:
TypedValue themedValue = new TypedValue();
this.getTheme().resolveAttribute(R.attr.myicon, themedValue, true);
Drawable mydrawable = AppCompatResources.getDrawable(this, themedValue.resourceId);
mTxtTitre.setCompoundDrawables(mydrawable, null, null, null);
In the examples this would be your activity. If you're not in an activity get a valid context.
It would seem as though you are trying to set the icon of your myDialog using a resource and are trying to access it through R.style but your other code segment leads me to believe that you have the resource located in R.drawable
With that in mind you should be able to get the effect you want with myDialog.setIcon(R.drawable.myicon);

Categories

Resources