Android: how to get value of an attribute in code? - android

I would like to retrieve the int value of textApperanceLarge in code. I believe that the below code is going in the right direction, but can't figure out how to extract the int value from the TypedValue.
TypedValue typedValue = new TypedValue();
((Activity)context).getTheme().resolveAttribute(android.R.attr.textAppearanceLarge, typedValue, true);

Your code only gets the resource ID of the style that the textAppearanceLarge attribute points to, namely TextAppearance.Large as Reno points out.
To get the textSize attribute value from the style, just add this code:
int[] textSizeAttr = new int[] { android.R.attr.textSize };
int indexOfAttrTextSize = 0;
TypedArray a = context.obtainStyledAttributes(typedValue.data, textSizeAttr);
int textSize = a.getDimensionPixelSize(indexOfAttrTextSize, -1);
a.recycle();
Now textSize will be the text size in pixels of the style that textApperanceLarge points to, or -1 if it wasn't set. This is assuming typedValue.type was of type TYPE_REFERENCE to begin with, so you should check that first.
The number 16973890 comes from the fact that it is the resource ID of TextAppearance.Large

Using
TypedValue typedValue = new TypedValue();
((Activity)context).getTheme().resolveAttribute(android.R.attr.textAppearanceLarge, typedValue, true);
For the string :
typedValue.string
typedValue.coerceToString()
For other data :
typedValue.resourceId
typedValue.data // (int) based on the type
In your case what it returns is of the TYPE_REFERENCE.
I know it should point to TextAppearance.Large
Which is :
<style name="TextAppearance.Large">
<item name="android:textSize">22sp</item>
<item name="android:textStyle">normal</item>
<item name="android:textColor">?textColorPrimary</item>
</style>
Credit goes to Martin for resolving this :
int[] attribute = new int[] { android.R.attr.textSize };
TypedArray array = context.obtainStyledAttributes(typedValue.resourceId, attribute);
int textSize = array.getDimensionPixelSize(0, -1);

Or in kotlin:
fun Context.dimensionFromAttribute(attribute: Int): Int {
val attributes = obtainStyledAttributes(intArrayOf(attribute))
val dimension = attributes.getDimensionPixelSize(0, 0)
attributes.recycle()
return dimension
}

It seems to be an inquisition on the #user3121370's answer. They burned down. :O
If you just need the get a dimension, like a padding, minHeight (my case was: android.R.attr.listPreferredItemPaddingStart). You can do:
TypedValue typedValue = new TypedValue();
((Activity)context).getTheme().resolveAttribute(android.R.attr.listPreferredItemPaddingStart, typedValue, true);
Just like the question did, and then:
final DisplayMetrics metrics = new android.util.DisplayMetrics();
WindowManager wm = (WindowManager)mContext.getSystemService(Context.WINDOW_SERVICE);
wm.getDefaultDisplay().getMetrics(metrics);
int myPaddingStart = typedValue.getDimension( metrics );
Just like the removed answer. This will allow you to skip handling device pixel sizes, because it uses the default device metric. The return will be float, and you should cast to int.
Becareful to the type you are trying to get, like resourceId.

this is my code.
public static int getAttributeSize(int themeId,int attrId, int attrNameId)
{
TypedValue typedValue = new TypedValue();
Context ctx = new ContextThemeWrapper(getBaseContext(), themeId);
ctx.getTheme().resolveAttribute(attrId, typedValue, true);
int[] attributes = new int[] {attrNameId};
int index = 0;
TypedArray array = ctx.obtainStyledAttributes(typedValue.data, attributes);
int res = array.getDimensionPixelSize(index, 0);
array.recycle();
return res;
}
// getAttributeSize(theme, android.R.attr.textAppearanceLarge, android.R.attr.textSize) ==> return android:textSize

Related

Error: Expected resource of type styleable [ResourceType] error

Take a look at this code snippet.
I am getting an error with the last line, because I am passing an 'index' instead of a resource. I thought it was a lint issue and tried to suppress it.
Then I noticed I am getting this error only when I building for release. It works fine when building for debug. I am totally clueless. Can anyone throw some light into what I am doing wrong.
//Get paddingLeft, paddingRight
int[] attrsArray = new int[]{
android.R.attr.paddingLeft, // 0
android.R.attr.paddingRight, // 1
};
TypedArray ta = context.obtainStyledAttributes(attrs, attrsArray);
if (ta == null) return;
mPaddingLeft = ta.getDimensionPixelSize(0, 0);
mPaddingRight = ta.getDimensionPixelSize(1/*error here*/, 0);
I had the same issue when trying to build a signed apk.
Solved it by adding #SuppressWarnings("ResourceType") to suppress the warning, now it works fine.
#StyleableRes int index = 1;
mPaddingRight = ta.getDimensionPixelSize(index, 0);
So the way I fixed this in Kotlin is the following: (thanks to Simons post)
companion object {
private val attributes = intArrayOf(
android.R.attr.paddingLeft,
android.R.attr.paddingTop,
android.R.attr.paddingBottom,
android.R.attr.paddingRight)
}
init {
val arr = context.obtainStyledAttributes(attrs, attributes)
#StyleableRes
var i = 0
val leftPadding = arr.getDimensionPixelOffset(i++, 0)
val topPadding = arr.getDimensionPixelOffset(i++, 0)
val rightPadding = arr.getDimensionPixelOffset(i++, 0)
val bottomPadding = arr.getDimensionPixelOffset(i, 0)
arr.recycle()
}
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.paddingLeft, outValue, true);
int paddingLeft = outValue.data;
Repeat the same for android.R.attr.paddingRight

Reference different raw resource depending on screen orientation

I would like to define raw resources in my dimens.xml file like I define margins, padding for different screen orientations.
I've tried this:
<item name="my_res" type="raw" format="string">R.raw.test</item>
But that doesn't seem to be working.
When I try to fetch id of that resource, it is not correct:
TypedValue out = new TypedValue();
getResources().getValue(R.raw.my_res, out, true);
int resId = out.resourceId;
Any suggestions how to han
Use Activity.getResources().getConfiguration().orientation for obtaining ORIENTATION_PORTRAIT and ORIENTATION_LANDSCAPE values indicating current orientation. Based on this, get appropiate raw resource.
TypedValue out = new TypedValue();
int resId;
if(Activity.getResources().getConfiguration().orientation == 1) { //1 for Portrait and 2 for Landscape
getResources().getValue(R.raw.my_res, out, true);
resId = out.resourceId;
} else {
getResources().getValue(R.raw.my_res_alternate, out, true);
resId = out.resourceId;
}
Do any necesary change, but this is the main idea.

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 get background color from current theme programmatically

I tried something like this, but i stuck:
TypedValue typedValue = new TypedValue();
if (this.parentActivity.getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true))
{
// how to get color?
}
You can get the background color (or Drawable) from the current theme by:
TypedValue a = new TypedValue();
getTheme().resolveAttribute(android.R.attr.windowBackground, a, true);
if (a.isColorType()) {
// windowBackground is a color
int color = a.data;
} else {
// windowBackground is not a color, probably a drawable
Drawable d = activity.getResources().getDrawable(a.resourceId);
}
isColorType was introduced in API level 29. Before then, you can use the following instead:
if (a.type >= TypedValue.TYPE_FIRST_COLOR_INT && a.type <= TypedValue.TYPE_LAST_COLOR_INT)
You can get the resources of your Theme by using :
TypedArray a = getTheme().obtainStyledAttributes(R.style.ThemeName, new int[] {R.attr.attribute_name});
int attributeResourceId = a.getResourceId(0, 0);
for your qoustion the easiest way is:
TypedValue typedValue = new TypedValue();
if (this.parentActivity.getTheme().resolveAttribute(android.R.attr.windowBackground, typedValue, true))
{
// how to get color?
int colorWindowBackground = typedValue.data;// **just add this line to your code!!**
}

Android: how to get value of "listPreferredItemHeight" attribute in code?

The below code gives Resources$NotFoundException
TypedValue value = new TypedValue();
((Activity)context).getResources().getValue(android.R.attr.listPreferredItemHeight, value, true);
EDIT: More code added in response to answer.
When I run the below code, all members of displayMetrics are 0. As is ret.
TypedValue value = new TypedValue();
DisplayMetrics displayMetrics = new DisplayMetrics();
((Activity)context).getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight, value, true);
float ret = value.getDimension(displayMetrics);
This works:
TypedValue value = new TypedValue();
((Activity)context).getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight, value, true);
EDIT: You get zero because haven't initialized the DisplayMetrics instance properly. It needs a frame of reference (a display) to do any meaningful conversion.
android.util.TypedValue value = new android.util.TypedValue();
boolean b = getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight, value, true);
String s = TypedValue.coerceToString(value.type, value.data);
android.util.DisplayMetrics metrics = new android.util.DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metrics);
float ret = value.getDimension(metrics);
On my Nexus 1 s is 64.0dip and ret is 96.
Another answer
public float getItemHeight() {
TypedValue value = new TypedValue();
DisplayMetrics metrics = new DisplayMetrics();
context.getTheme().resolveAttribute(
android.R.attr.listPreferredItemHeight, value, true);
((WindowManager) (context.getSystemService(Context.WINDOW_SERVICE)))
.getDefaultDisplay().getMetrics(metrics);
return TypedValue.complexToDimension(value.data, metrics);
}
it maybe more useful.
Femi's answer was very helpful. Without wanting to detract from his answer, I've taken the logic and placed it in a library convenience method that you should be able to plug-and-play. I plan on updating the code with other attribute methods over time. I hope it proves useful to someone.
(Note that I discovered Resources.getDisplayMetrics() seems to be an easier way to return display metrics rather than querying the WindowManager.)
The shortest answer (without DisplayMetrics):
TypedValue typedValue = new TypedValue();
context.getTheme().resolveAttribute(android.R.attr.listPreferredItemHeight, typedValue, true);
int height = TypedValue.complexToDimensionPixelSize(typedValue.data, context.getResources().getDisplayMetrics());

Categories

Resources