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

93

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

然而,这个WebView没有样式,因此它看起来与通常的TextView非常不同。我读过可以通过直接插入一些HTML来简单地为WebView中的文本设置样式。这听起来很容易,但我想使用我的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>

我主要关注文本大小和文本颜色。


为什么不直接解析XML(例如Android的SAX解析器)? - m0skit0
6个回答

198

可以通过编程方式从styles.xml中检索自定义样式。

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>

现在,像这样检索样式
// 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()

4
样式中定义的值是否也能在运行时更改? - ekjyot
3
我对这行代码有问题:'TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, attrs);',Android Studio告诉我它期望的资源类型是styleable,并且我看不到任何方法可以抑制支持库中的注释。我猜测obtainStyledAttributes具有@ResStyleable注释,这会阻止我将样式作为整数传递。你有什么想法吗? - Ben Pearson
2
@BenPearson - 我和你一样遇到了获取textSize属性的问题。PrivatMamtora的回答可行且应该被采纳为正确答案。 - thecoolmacdude
@Ole - 很多用户都在问,在获取值之后我们能否更改它?如果可以,请告诉我们如何操作? - Ajay Sharma
1
这个代码完美运行,除非你使用了一些属性,比如android:fontFamily。为了解决这个问题,你需要按升序排列attrs数组!(我知道这听起来很奇怪)。请参考这篇文章:https://medium.com/iloveapp/elegant-way-to-use-custom-standard-attributes-on-android-d1242bdfa62a@Ole,也许可以更新答案并加入这个补充? :) - crysxd
显示剩余5条评论

58

@Ole 给出的答案似乎在使用某些属性(例如shadowColor,shadowDx,shadowDy,shadowRadius)时会出问题(这只是我发现的几个,可能还有更多)。

我不知道为什么会出现这个问题,我在这里提出了疑问,但是@AntoineMarques的编码风格似乎可以解决这个问题。

要使其适用于任何属性,可以像这样定义一个可定制样式以包含资源id:


首先,在attrs.xml中定义一个可定制的样式,如下所示:

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

然后在代码中,您需要执行以下操作才能获取文本。

TypedArray ta = obtainStyledAttributes(R.style.MyCustomStyle, R.styleable.MyStyle);  
String text = ta.getString(R.styleable.MyStyle_android_text);
使用这种方法的优点是,您通过名称而不是索引检索值。

3
这应该成为被接受的答案,因为Ole的答案在我的代码中引发了一个“预期资源类型为styleable”的警告。 - thecoolmacdude
1
对我来说可以工作,但是在使用Android Studio时,我需要在前面加上@SuppressWarnings("ResourceType") - eruve
2
这个答案应该被接受。它适用于我所有的属性,而得票最高的那个浪费了我很多时间。 - jerry
1
感谢强大的海豚为您提供解决方案。被接受的答案对于像背景(使用drawable时)和文本大小这样的属性给我带来了问题。这个解决方案不仅适用于我需要的所有属性,而且还提供了更清晰的代码。 - Marius P.
3
R.style.MyCustomStyle应该是什么? - anon
显示剩余2条评论

5

我无法让之前的解决方案生效。

我的风格是:

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

使用android.R.attr.textSize的obtainStyledAttributes()方法返回String类型的结果,例如"12sp",需要进行解析。而使用android.R.attr.textColor时,它会返回一个资源文件XML名称。这些都太繁琐了。
相反,我找到了一种更简单的方法,可以使用ContextThemeWrapper。
TextView sample = new TextView(new ContextThemeWrapper(getContext(), R.style.Widget_TextView_NumPadKey_Klondike), null, 0);

这使我得到了一个完全样式化的TextView,可以查询任何我想要的内容。例如:
float textSize = sample.getTextSize();

3
和的答案对我来说效果不太好,但是这个可以。 <让我们假设我想通过编程方式读取这个样式:

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

我可以这样做:
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()
}

我参考了Android的TextAppearanceSpan类,你可以在这里找到它:https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/text/style/TextAppearanceSpan.java

1
使用 Kotlin,如果在您的应用程序/库中包含 androidx.core:core-ktx 库...
implementation("androidx.core:core-ktx:1.6.0") // Note the -ktx

你可以选择以下任意一种方式(无需回收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
}

注1(感谢this answer

为了获取枚举值,您可以定义此扩展函数:

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 }

注意 2

-ktx 依赖项(如 androidx.core:coreandroidx.core:core-ktx)之间的区别在于 -ktx 变体包含 Kotlin 的有用扩展函数。否则,它们是相同的。

此外,感谢 Ole 的答案


-5
如果接受的解决方案不起作用,请尝试将attr.xml重命名为attrs.xml(对我有用)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接