如何以编程方式从 styles.xml 检索样式属性

目前,我正在使用 WebView 或 TextView 来显示一些来自我的应用程序中的 Web 服务的动态数据。 如果数据包含纯文本,它将使用 TextView 并应用 styles.xml 中的样式。 如果数据包含 HTML (主要是文本和图像) ,则使用 WebView。

然而,这个 WebView 是没有样式的,因此它看起来与通常的 TextView 有很大的不同。 我曾经读到过在 WebView 中可以通过简单地将一些 HTML 直接插入到数据中来对文本进行样式化。这听起来很简单,但是我想使用 Styles.xml 中的数据作为这个 HTML 中所需的值,所以如果我改变样式的话,我不需要在两个位置上改变颜色等等。

那么,我要怎么做呢?我做了一些广泛的搜索,但是没有找到从 styles.xml 中检索不同样式属性的实际方法。是我遗漏了什么,还是真的不可能检索到这些值?

我试图从以下样式中获得数据:

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

我主要对 textSize 和 textColor 感兴趣。

67664 次浏览

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()

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.

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

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

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.

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();