我正在尝试找到一种方法来获取属性的原始未解析值。这样,我的意思是布局文件中的确切文本,而不必直接解析该文件。
说有一个像这样的TextView:
<TextView
...
android:textColor="?colorAccent"
/>
我希望能够提取“?colorAccent” 作为字符串,或值的条目名称 (例如package:attr / colorAccent )。
如果没有XMLPullParser,这是否可能?
答案 0 :(得分:1)
在这些示例中,我使用的视图标签如下:
<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()
开始。对于纯文本属性,这将为您提供实际值(例如,对于android:hint
,它返回"this is the hint"
)。
Resource属性返回一个以@
开头,然后为数字的字符串(例如,对于android:text
,它返回"@2131689506"
)。然后,您可以解析此字符串的数字部分,并使用Resources.getResourceName()
获取解析的名称("com.example.stackoverflow:string/dummy_content"
)。
样式属性返回以?
开头且为数字的字符串(例如,对于android:textColor
,它返回"?2130903135"
)。但是,我不知道有任何方法可以将此数字转换为带有受支持的API的文本表示形式。不过,希望这足以帮助其他人获得完整答案。
但是,如果您愿意摆脱困境,则可以使用反射来查找样式属性的文本值。因为字符串以?
开头,所以您知道它在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
答案 1 :(得分:0)
在@Ben P.提供的内容上,我能够提出一些不使用反射的内容:
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)
}
}
}
文本视图的文本将设置为布局XML中提供的Android文本颜色属性的“原始值”。
如果您的文本视图的文本设置为AndroidX的?colorAccent
,则上面的代码将使用文本[your-package]:attr/colorAccent
填充文本视图。它将对资源执行相同的操作,返回包括包的整个条目名称。如果该属性设置为文字文本,则只会返回该文本。