代码之家  ›  专栏  ›  技术社区  ›  afollestad

如何从属性集获取属性的原始未解析值?

  •  2
  • afollestad  · 技术社区  · 6 年前

    我试图找到一种方法来获取属性的原始未解析值。我指的是布局文件中几乎完全相同的文本,而不必直接解析文件。

    <TextView
        ...
        android:textColor="?colorAccent"
        />
    

    “?颜色强调” 或值的条目名称

    如果没有XMLPullParser,这是可能的吗?

    2 回复  |  直到 6 年前
        1
  •  1
  •   Ben P.    6 年前

    对于以下示例,我使用的视图标记如下所示:

    <EditText 
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:hint="this is the hint"
        android:text="@string/dummy_content"
        android:textColor="?colorAccent" />
    

    android:hint 是纯文本, android:text 是资源属性,并且 android:textColor 是样式属性。

    AttributeSet.getAttributeValue() . 对于纯文本属性,这将为您提供实际值(例如 这是回报 "this is the hint"

    资源属性返回一个以 @ 这是回报 "@2131689506" ). 然后可以解析这个字符串的数字部分并使用 Resources.getResourceName() 获取解析名称( "com.example.stackoverflow:string/dummy_content"

    样式属性返回以 ? android:textColor 这是回报 "?2130903135" ). 但是,我不知道有什么方法可以用支持的api将这个数字转换成文本表示。不过,希望这足以帮助其他人找到完整答案。

    使用反射

    但是,如果您愿意偏离轨道,可以使用反射来查找style属性的文本值。因为字符串以 R.attr android.R.attr . 您可以使用如下代码扫描这些字段以查找匹配的字段:

    private static String scan(Class<?> classToSearch, int target) {
        for (Field field : classToSearch.getDeclaredFields()) {
            try {
                int fieldValue = (int) field.get(null);
    
                if (fieldValue == target) {
                    return field.getName();
                }
            } catch (IllegalAccessException e) {
               // TODO
            }
        }
    
        return null;
    }
    
    int id = Integer.parseInt(attributeValue.substring(1));
    String attrName = scan(R.attr.class, id);
    String androidAttrName = scan(android.R.attr.class, id);
    

    对我来说,这将输出

    colorAccent
    null
    

    如果 android:textColor ?android:colorAccent ?colorAccent ,则输出为:

    null
    colorAccent
    
        2
  •  0
  •   afollestad    6 年前

    class Attributes(
      private val context: Context,
      private val attrs: AttributeSet
    ) {
      fun getRawValue(@AttrRes attrId: Int): String {
        val res = context.resources
        val attrName = res.getResourceName(attrId)
        val attrIndex = attrs.indexOfAttr(context) { it == attrName }
        if (attrIndex == -1) return ""
        val attrValue = attrs.getAttributeValue(attrIndex)
    
        return when {
          attrValue.startsWith('@') || attrValue.startsWith('?') -> {
            val id = attrValue.substring(1)
                .toInt()
            res.getResourceName(id)
          }
          else -> attrValue
        }
      }
    }
    
    private fun AttributeSet.indexOfAttr(
      context: Context,
      matcher: (String) -> (Boolean)
    ): Int {
      for (i in 0 until attributeCount) {
        val nameResource = getAttributeNameResource(i)
        val literalName = if (nameResource != 0) context.resources.getResourceName(nameResource) else ""
        if (matcher(literalName)) {
          return i
        }
      }
      return -1
    }
    

    class MyTextView(
      context: Context, 
      attrs: AttributeSet? = null
    ) : AppCompatTextView() {
    
      init {
         if (attrs != null) {
             val attributes = Attributes(context, attrs)
             val rawTextColor = attributes.getRawValue(android.R.attr.textColor)
             setText(rawTextColor)
         }
      }
    }
    

    文本视图的文本将被设置为Android的文本颜色属性的“原始值”,该属性在布局XML中提供。

    如果文本视图的文本设置为AndroidX的 ?colorAccent ,上面的代码将用文本填充文本视图 [your-package]:attr/colorAccent . 它将对资源执行相同的操作,返回包括包在内的整个条目名称。如果属性被设置为literal text,它只会返回这个值。