95

Currently I'm using either a WebView or a TextView to show some dynamic data coming from a webservice in one of my apps. If the data contains pure text, it uses the TextView and applies a style from styles.xml. If the data contains HTML (mostly text and images) it uses the WebView.

However, this WebView is unstyled. Therefor it looks a lot different from the usual TextView. I've read that it's possible to style the text in a WebView simply by inserting some HTML directly into the data. This sounds easy enough, but I would like to use the data from my Styles.xml as the values required in this HTML so I won't need to change the colors et cetera on two locations if I change my styles.

So, how would I be able to do this? I've done some extensive searching but I have found no way of actually retrieving the different style attributes from your styles.xml. Am I missing something here or is it really not possible to retrieve these values?

The style I'm trying to get the data from is the following:

<style name="font4">
    <item name="android:layout_width">fill_parent</item>
    <item name="android:layout_height">wrap_content</item>
    <item name="android:textSize">14sp</item>
    <item name="android:textColor">#E3691B</item>
    <item name="android:paddingLeft">5dp</item>
    <item name="android:paddingRight">10dp</item>
    <item name="android:layout_marginTop">10dp</item>
    <item name="android:textStyle">bold</item>
</style>

I'm mainly interested in the textSize and textColor.

1
  • Why don't you just parse the XML (e.g. Android's SAX parser)? Commented Dec 19, 2012 at 11:14

6 Answers 6

200
+100

It is possible to retrieve custom styles from styles.xml programmatically.

Define some arbitrary style in styles.xml:

<style name="MyCustomStyle">
    <item name="android:textColor">#efefef</item>
    <item name="android:background">#ffffff</item>
    <item name="android:text">This is my text</item>
</style>

Now, retrieve the styles like this

// The attributes you want retrieved
int[] attrs = {android.R.attr.textColor, android.R.attr.background, android.R.attr.text};

// Parse MyCustomStyle, using Context.obtainStyledAttributes()
TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, attrs);

// Fetch the text from your style like this.     
String text = ta.getString(2);

// Fetching the colors defined in your style
int textColor = ta.getColor(0, Color.BLACK);
int backgroundColor = ta.getColor(1, Color.BLACK);

// Do some logging to see if we have retrieved correct values
Log.i("Retrieved text:", text);
Log.i("Retrieved textColor as hex:", Integer.toHexString(textColor));
Log.i("Retrieved background as hex:", Integer.toHexString(backgroundColor));

// OH, and don't forget to recycle the TypedArray
ta.recycle()
Sign up to request clarification or add additional context in comments.

10 Comments

is it also possible to change the values that are defined in style at run time?
What if I want to change the color of the attributes retrieved? Like changing the background or text color dynamically within this code?
I have a problem with this line: 'TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, attrs);', Android studio is telling me it expects resource of type styleable and I can't see any way to suppress the annotations in the support library. I assume that obtainStyledAttributes has a @ResStyleable annotation which is preventing me passing a style as an int through. Any ideas?
@BenPearson - I had the same issue as you trying to get the textSize attribute. PrivatMamtora's answer works and should be the accepted answer.
This works perfectly fine unless you use some attributes like android:fontFamily. To fix this, you need to sort the attrs array in ascending order! (I know it sounds weird af). See this medium: medium.com/iloveapp/… @Ole, maybe update the answer with this addition? :)
|
61

The answer @Ole has given seems to break when using certain attributes, such as shadowColor, shadowDx, shadowDy, shadowRadius (these are only a few I found, there might be more)

I have no idea as to why this issue occurs, which I am asking about here, but @AntoineMarques coding style seems to solve the issue.

To make this work with any attribute it would be something like this


First, define a stylable to contain the resource ids like so

attrs.xml

<resources>     
    <declare-styleable name="MyStyle" >
        <attr name="android:textColor" />
        <attr name="android:background" />
        <attr name="android:text" />
    </declare-styleable>
</resources>

Then in code you would do this to get the text.

TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, R.styleable.MyStyle);  
String text = ta.getString(R.styleable.MyStyle_android_text);

The advantage of using this method is, you are retrieving the value by name and not an index.

7 Comments

This should be the accepted answer as Ole's answer generated a "expected resource of type styleable" warning for me.
Works for me, but with Android Studio I need to prefix that with a @SuppressWarnings("ResourceType").
This answer should be the accepted one. It's work for all my attribute, and the most voted one wasted me much time.
Thank the mighty dolphin for your solution. The accepted answer created problems for me for attributes like background (when using drawable) and text size. This solution not only works for all attributes I needed, but also gives a cleaner code.
What is R.style.MyCustomStyle supposed to be?
|
6

I was not able to get the earlier solutions to work.

My style is:

<style name="Widget.TextView.NumPadKey.Klondike" parent="Widget.TextView.NumPadKey">
    <item name="android:textSize">12sp</item>
    <item name="android:fontFamily">sans-serif</item>
    <item name="android:textColor">?attr/wallpaperTextColorSecondary</item>
    <item name="android:paddingBottom">0dp</item>
</style>

The obtainStyledAttributes() for android.R.attr.textSize gives String results of "12sp" which I then have to parse. For android.R.attr.textColor it gave a resource file XML name. This was much too cumbersome.

Instead, I found an easy way using ContextThemeWrapper.

TextView sample = new TextView(new ContextThemeWrapper(getContext(), R.style.Widget_TextView_NumPadKey_Klondike), null, 0);

This gave me a fully-styled TextView to query for anything I want. For example:

float textSize = sample.getTextSize();

Comments

3

The answers from Ole and PrivatMamtora didn't work well for me, but this did.

Let's say I wanted to read this style programmatically:

<style name="Footnote">
    <item name="android:fontFamily">@font/some_font</item>
    <item name="android:textSize">14sp</item>
    <item name="android:textColor">@color/black</item>
</style>

I could do it like this:

fun getTextColorSizeAndFontFromStyle(
    context: Context, 
    textAppearanceResource: Int // Can be any style in styles.xml like R.style.Footnote
) {
    val typedArray = context.obtainStyledAttributes(
        textAppearanceResource,
        R.styleable.TextAppearance // These are added to your project automatically.
    )
    val textColor = typedArray.getColorStateList(
        R.styleable.TextAppearance_android_textColor
    )
    val textSize = typedArray.getDimensionPixelSize(
        R.styleable.TextAppearance_android_textSize
    )

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        val typeface = typedArray.getFont(R.styleable.TextAppearance_android_fontFamily)

        // Do something with the typeface...

    } else {
        val fontFamily = typedArray.getString(R.styleable.TextAppearance_fontFamily)
            ?: when (typedArray.getInt(R.styleable.TextAppearance_android_typeface, 0)) {
                1 -> "sans"
                2 -> "serif"
                3 -> "monospace"
                else -> null
            }

        // Do something with the fontFamily...
    }
    typedArray.recycle()
}

I took some inspiration from Android's TextAppearanceSpan class, you can find it here: https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/style/TextAppearanceSpan.java

Comments

1

With Kotlin, if you include the androidx.core:core-ktx library in your app/library...

implementation("androidx.core:core-ktx:1.6.0") // Note the -ktx

...you can have either of the following (no need for you to recycle the TypedArray):

// Your desired attribute IDs
val attributes = intArrayOf(R.attr.myAttr1, R.attr.myAttr2, android.R.attr.text)

context.withStyledAttributes(R.style.MyStyle, attributes) {
    val intExample = getInt(R.styleable.MyIntAttrName, 0)
    val floatExample = getFloat(R.styleable.MyFloatAttrName, 0f)
    val enumExample = R.styleable.MyEnumAttrName, MyEnum.ENUM_1 // See Note 1 below
    // Similarly, getColor(), getBoolean(), etc.
}
context.withStyledAttributes(R.style.MyStyle, R.styleable.MyStyleable) {
    // Like above
}
// attributeSet is provided to you like in the constructor of a custom view
context.withStyledAttributes(attributeSet, R.styleable.MyStyleable) {
    // Like above
}

Note 1 (thanks to this answer)

For getting an enum value you can define this extension function:

internal inline fun <reified T : Enum<T>> TypedArray.getEnum(index: Int, default: T) =
    getInt(index, -1).let { if (it >= 0) enumValues<T>()[it] else default }

Note 2

The difference between -ktx dependencies like androidx.core:core and androidx.core:core-ktx is that the -ktx variant includes useful extension functions for Kotlin. Otherwise, they are the same.

Also, thanks to the answer by Ole.

Comments

-5

If accepted solution not working for try to rename attr.xml to attrs.xml (worked for me)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.