在自定义 lint 规则中解析资源值
Resolving resource values in custom lint rule
我有一个很大的 Android 代码库,我正在编写一个自定义 lint 规则来检查某些属性的值是否在给定范围内。
例如,我有这个组件:
<MyCustomComponent
my:animation_factor="0.7"
...>
</MyCustomComponent>
并且我想编写一个 lint 规则,提醒开发人员应谨慎使用 my:animation_factor
>= 1 的值。
我按照 http://tools.android.com/tips/lint-custom-rules 中的说明进行操作,并使用以下代码设法检索了 my:animation_factor
的值:
import com.android.tools.lint.detector.api.*;
public class XmlInterpolatorFactorTooHighDetector {
....
@Override
public Collection<String> getApplicableElements() {
return ImmutableList.of("MyCustomComponent");
}
@Override
public void visitElement(XmlContext context, Element element) {
String factor = element.getAttribute("my:animation_factor");
...
if (value.startsWith("@dimen/")) {
// How do I resolve @dimen/xyz to 1.85?
} else {
String value = Float.parseFloat(factor);
}
}
}
当 my:animation_factor
等属性具有文字值(例如 0.7
)时,此代码可以正常工作。
但是,当属性值是资源时(例如 @dimen/standard_anim_factor
),则 element.getAttribute(...)
returns 属性的字符串值而不是实际解析值。
例如,当我有一个看起来像这样的 MyCustomComponent
时:
<MyCustomComponent
my:animation_factor="@dimen/standard_anim_factory"
...>
</MyCustomComponent>
和@dimen/standard_anim_factor
在别处定义:
<dimen name="standard_anim_factor">1.85</dimen>
然后字符串 factor
变为“@dimen/standard_anim_factor”而不是“1.85”。
有没有办法在处理 MyCustomComponent
元素时将“@dimen/standard_anim_factor”解析为资源的实际值(即“1.85”)?
我相信您正在寻找 getResources().getDimension()
。
来源:http://developer.android.com/reference/android/content/res/Resources.html#getDimension%28int%29
假设 xml 节点在解析你的数据后,尝试以下
Element element = null; //It is your root node.
NamedNodeMap attrib = (NamedNodeMap) element;
int numAttrs = attrib.getLength ();
for (int i = 0; i < numAttrs; i++) {
Attr attr = (Attr) attrib.item (i);
String attrName = attr.getNodeName ();
String attrValue = attr.getNodeValue ();
System.out.println ("Found attribute: " + attrName + " with value: " + attrValue);
}
值解析的一般问题是,它们取决于您所在的 Android 运行时上下文。可能有几个 values
文件夹具有不同的密钥具体值 @dimen/standard_anim_factory
,所以你知道。
然而,AFAIK 存在两个选项:
执行两相检测:
- 第 1 阶段:扫描您的资源
- 扫描您的属性并将其放入列表中(而不是立即对其进行评估)
- 扫描您的维度值并将它们也放入列表中
- 第 2 阶段:
- 覆盖
Detector.afterProjectCheck
并通过遍历阶段 1 中填充的两个列表来解析您的属性
通常 LintUtils
class [1] 是处理这些问题的最佳地点,但不幸的是没有解决维度值的方法。但是,有一个名为 getStyleAttributes
的方法演示了如何解析资源值。因此,您可以编写自己方便的方法来解析维度值:
private int resolveDimensionValue(String name, Context context){
LintClient client = context.getDriver().getClient();
LintProject project = context.getDriver().getProject();
AbstractResourceRepository resources = client.getProjectResources(project, true);
return Integer.valueOf(resources.getResourceItem(ResourceType.DIMEN, name).get(0).getResourceValue(false).getValue());
}
注意:上面的代码我还没有测试过。所以请将其视为理论建议:-)
关于您的自定义 Lint 规则代码的一点小建议,因为您只对属性感兴趣:
而不是在 visitElement
中做这样的事情:
String factor = element.getAttribute("my:animation_factor");
...你可能想做这样的事情:
@Override
public Collection<String> getApplicableAttributes() {
return ImmutableList.of("my:animation_factor");
}
@Override
void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute){
...
}
但这只是个人喜好问题:-)
我有一个很大的 Android 代码库,我正在编写一个自定义 lint 规则来检查某些属性的值是否在给定范围内。
例如,我有这个组件:
<MyCustomComponent
my:animation_factor="0.7"
...>
</MyCustomComponent>
并且我想编写一个 lint 规则,提醒开发人员应谨慎使用 my:animation_factor
>= 1 的值。
我按照 http://tools.android.com/tips/lint-custom-rules 中的说明进行操作,并使用以下代码设法检索了 my:animation_factor
的值:
import com.android.tools.lint.detector.api.*;
public class XmlInterpolatorFactorTooHighDetector {
....
@Override
public Collection<String> getApplicableElements() {
return ImmutableList.of("MyCustomComponent");
}
@Override
public void visitElement(XmlContext context, Element element) {
String factor = element.getAttribute("my:animation_factor");
...
if (value.startsWith("@dimen/")) {
// How do I resolve @dimen/xyz to 1.85?
} else {
String value = Float.parseFloat(factor);
}
}
}
当 my:animation_factor
等属性具有文字值(例如 0.7
)时,此代码可以正常工作。
但是,当属性值是资源时(例如 @dimen/standard_anim_factor
),则 element.getAttribute(...)
returns 属性的字符串值而不是实际解析值。
例如,当我有一个看起来像这样的 MyCustomComponent
时:
<MyCustomComponent
my:animation_factor="@dimen/standard_anim_factory"
...>
</MyCustomComponent>
和@dimen/standard_anim_factor
在别处定义:
<dimen name="standard_anim_factor">1.85</dimen>
然后字符串 factor
变为“@dimen/standard_anim_factor”而不是“1.85”。
有没有办法在处理 MyCustomComponent
元素时将“@dimen/standard_anim_factor”解析为资源的实际值(即“1.85”)?
我相信您正在寻找 getResources().getDimension()
。
来源:http://developer.android.com/reference/android/content/res/Resources.html#getDimension%28int%29
假设 xml 节点在解析你的数据后,尝试以下
Element element = null; //It is your root node.
NamedNodeMap attrib = (NamedNodeMap) element;
int numAttrs = attrib.getLength ();
for (int i = 0; i < numAttrs; i++) {
Attr attr = (Attr) attrib.item (i);
String attrName = attr.getNodeName ();
String attrValue = attr.getNodeValue ();
System.out.println ("Found attribute: " + attrName + " with value: " + attrValue);
}
值解析的一般问题是,它们取决于您所在的 Android 运行时上下文。可能有几个 values
文件夹具有不同的密钥具体值 @dimen/standard_anim_factory
,所以你知道。
然而,AFAIK 存在两个选项:
执行两相检测:
- 第 1 阶段:扫描您的资源
- 扫描您的属性并将其放入列表中(而不是立即对其进行评估)
- 扫描您的维度值并将它们也放入列表中
- 第 2 阶段:
- 覆盖
Detector.afterProjectCheck
并通过遍历阶段 1 中填充的两个列表来解析您的属性
通常
LintUtils
class [1] 是处理这些问题的最佳地点,但不幸的是没有解决维度值的方法。但是,有一个名为getStyleAttributes
的方法演示了如何解析资源值。因此,您可以编写自己方便的方法来解析维度值:
private int resolveDimensionValue(String name, Context context){
LintClient client = context.getDriver().getClient();
LintProject project = context.getDriver().getProject();
AbstractResourceRepository resources = client.getProjectResources(project, true);
return Integer.valueOf(resources.getResourceItem(ResourceType.DIMEN, name).get(0).getResourceValue(false).getValue());
}
注意:上面的代码我还没有测试过。所以请将其视为理论建议:-)
关于您的自定义 Lint 规则代码的一点小建议,因为您只对属性感兴趣:
而不是在 visitElement
中做这样的事情:
String factor = element.getAttribute("my:animation_factor");
...你可能想做这样的事情:
@Override
public Collection<String> getApplicableAttributes() {
return ImmutableList.of("my:animation_factor");
}
@Override
void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute){
...
}
但这只是个人喜好问题:-)