【问题标题】:How to retrieve style attributes programmatically from styles.xml如何以编程方式从 styles.xml 检索样式属性
【发布时间】:2012-11-23 00:50:33
【问题描述】:

目前我正在使用 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>

我主要对 textSize 和 textColor 感兴趣。

【问题讨论】:

  • 为什么不直接解析 XML(例如 Android 的 SAX 解析器)?

标签: android xml styles


【解决方案1】:

可以通过编程方式从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()

【讨论】:

  • 是否也可以在运行时更改 style 中定义的值?
  • 如果我想更改检索到的属性的颜色怎么办?喜欢在此代码中动态更改背景或文本颜色?
  • 我对这一行有疑问:'TypedArray ta = gainStyledAttributes(R.style.MyCustomStyle, attrs);',Android 工作室告诉我它需要可样式的资源,但我看不到任何方式来抑制支持库中的注释。我假设 gainStyledAttributes 有一个 @ResStyleable 注释,它阻止我将样式作为 int 传递。有什么想法吗?
  • @BenPearson - 我遇到了与您尝试获取 textSize 属性相同的问题。 PrivatMamtora 的答案有效,应该是公认的答案。
  • 除非你使用android:fontFamily 之类的属性,否则它工作得非常好。要解决此问题,您需要按升序对 attrs 数组进行排序! (我知道这听起来很奇怪)。看到这个媒体:medium.com/iloveapp/…@Ole,也许用这个添加来更新答案? :)
【解决方案2】:

@Ole 给出的答案似乎在使用某些属性时会中断,例如 shadowColor、shadowDx、shadowDy、shadowRadius(这些只是我找到的几个,可能还有更多)

我不知道为什么会出现这个问题,我问的是here,但@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);

使用这种方法的好处是,您可以通过名称而不是索引来检索值。

【讨论】:

  • 这应该是公认的答案,因为 Ole 的答案为我生成了“预期的可样式化资源”警告。
  • 对我有用,但对于 Android Studio,我需要在前面加上 @SuppressWarnings("ResourceType")。
  • 这个答案应该是被接受的。这对我的所有属性都有效,投票最多的那一项浪费了我很多时间。
  • 感谢强大的海豚您的解决方案。接受的答案给我带来了背景(使用可绘制时)和文本大小等属性的问题。该解决方案不仅适用于我需要的所有属性,而且还提供了更简洁的代码。
  • R.style.MyCustomStyle 应该是什么?
【解决方案3】:

Ole 和 PrivatMamtora 的答案对我来说效果不佳,但确实如此。

假设我想以编程方式阅读这种风格:

<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

【讨论】:

    【解决方案4】:

    我无法让早期的解决方案发挥作用。

    我的风格是:

    <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() 给出“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();
    

    【讨论】:

      【解决方案5】:

      对于 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:core 和 androidx.core:core-ktx 的区别> 是 -ktx 变体包含对 Kotlin 有用的扩展功能。 否则,它们是相同的。

      另外,感谢the answer by Ole。

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-03-15
          • 1970-01-01
          • 2012-11-01
          • 2011-01-02
          相关资源
          最近更新 更多