上篇博客中讲了LayoutInflater.inflate
机制,其中提到了AttributeSet
和XmlPullParser
两个接口,这里我们来详细的了解一下Android中提供的AttributeSet
接口和它与XmlPullParser
的区别,以及如何使用TypedArray
获取AttributeSet
中对应的属性。
AttributeSet
是xml文件中元素属性的一个集合。其中提供了各种Api,供我们从已编译好的xml文件获取属性值,如getAttributeIntValue
,getAttributeBooleanValue
,getAttributeFloatValue
等,会返回对应类型的属性值,传入的参数一般有两种形式,如下:
getAttributeXXXValue(int index, XXX defaultValue)
:根据对应属性的索引获取对应的属性值,index取值范围在0~count-1之间,找不到返回defaultValue
getAttributeXXXValue(String namespace, String attribute, XXX defaultValue)
:根据指定命名空间的属性名获取对应的属性值,找不到返回defaultValue
我们现在知道了AttributeSet也是获取xml文件中属性值用的接口,那么它和XmlPullParser有什么关联和区别呢?
我们先看下下面这张类图:
XmlPullParser和AttributeSet都能从Xml文件中获取数据,它们的相同点为:
getAttributeName
,getAttributeValue
等。区别在于:
getAttributeXXXValue
的方法(如getAttributeIntValue),这些方法能返回对应XXX的类型值(如int值),而XmlPullParser获取属性值只能通过getAttributeValue
方法,返回值只能是String类型需要注意的是,在Android中,对于AttributeSet接口和XmlPullParser接口,其实现都是结合已有的编译好的xml资源,这些资源是编译时经过aapt生成的高度优化的资源,而不是通过pull方式解析原有的Xml字符串。这与我们常见的一般的XmlPullParser接口的实现机制(kXml,WbXml等)不同。
我们从类图中可以看到实现类有两个,分别是Parser
和XmlPullAttributes
,我们可以看下XmlPullAttributes
的具体实现:
class XmlPullAttributes implements AttributeSet {
XmlPullParser mParser;
public XmlPullAttributes(XmlPullParser parser) {
mParser = parser;
}
public int getAttributeCount() {
return mParser.getAttributeCount();
}
public String getAttributeNamespace (int index) {
return mParser.getAttributeNamespace(index);
}
public String getAttributeName(int index) {
return mParser.getAttributeName(index);
}
public String getAttributeValue(int index) {
return mParser.getAttributeValue(index);
}
public String getAttributeValue(String namespace, String name) {
return mParser.getAttributeValue(namespace, name);
}
public String getPositionDescription() {
return mParser.getPositionDescription();
}
public int getAttributeNameResource(int index) {
return 0;
}
public int getAttributeListValue(String namespace, String attribute,
String[] options, int defaultValue) {
return XmlUtils.convertValueToList(
getAttributeValue(namespace, attribute), options, defaultValue);
}
//...其余AttributeSet中的接口都是类似getAttributeListValue通过XmlUtils转换类型来实现的
}
可以看到XmlPullAttributes对于AttributeSet中和XmlPullParser相同的接口都是通过内部的XmlPullParser变量来实现的。
XmlPullParser还可以通过以下方式生成对应的AttributeSet:
public static AttributeSet asAttributeSet(XmlPullParser parser) {
return (parser instanceof AttributeSet)
? (AttributeSet) parser
: new XmlPullAttributes(parser);
}
然而,我们很少用到AttributeSet类提供的方法来获取它的属性,常常都是通过Theme.obtainStyledAttributes方法来获取其中的属性的,方法如下:
public TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)
获取到TypedArray之后,再去调用TypedArray的Api来处理。
为什么不直接用AttributeSet来解析属性值呢?
我们可以看下面这个xml布局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="@dimen/w_50dp"
android:layout_height="@dimen/w_50dp"
android:orientation="vertical"/>
我们通过AttributeSet
直接去获取属性的话:
final XmlResourceParser parser = getResources().getLayout(R.layout.ll);
int type;
while ((type = parser.getEventType()) != XmlPullParser.START_TAG
&& type != XmlPullParser.END_DOCUMENT) {
parser.next();
}
int count = parser.getAttributeCount();
Log.d(TAG, "count: " + parser.getAttributeCount());
AttributeSet attributeSet = Xml.asAttributeSet(parser);
for (int i = 0; i < count; i++) {
String name = attributeSet.getAttributeName(i);
String value = attributeSet.getAttributeValue(i);
Log.d(TAG, "attribute: " + name + " value: " + value);
}
int resourceId = attributeSet.getAttributeResourceValue("http://schemas.android.com/apk/res/android", "layout_height", 0);
float dimension = getResources().getDimension(resourceId);
Log.d(TAG, "layout_height: " + dimension);
得到的结果如下:
count: 3
attribute: orientation value: 1
attribute: layout_width value: @2131034226
attribute: layout_height value: @2131034226
layout_height: 150.0 #得到的是px
可以看到如果直接使用AttributeSet
来获取属性值有以下缺点:
getAttributeValue
获得的只是资源的id,虽然可以通过getAttributeResourceValue
获取到id后,通过Resources
类来获取对应的属性值,但是这样会有多步的操作。我们来看看使用TypedArray是如何获取属性值的。TypedArray是一个存放属性值数组的一个容器,通常通过如下方式来获得TypedArray的:
Resources.Theme.obtainStyledAttributes(AttributeSet set,
@StyleableRes int[] attrs,
@AttrRes int defStyleAttr,
@StyleRes int defStyleRes)
我们来分别看下每个参数的含义:
AttributeSet set
:表示从AttributeSet中挑选属性,可以为空int[] attrs
:表示你想挑选的属性,你想得到哪些属性,你就可以将其写到这个int数组中int defStyleAttr
:表示从defStyleAttr中挑选属性,可以为空int defStyleRes
:表示从defStyleRes中挑选属性,可以为空这里我们先来演示下从AttributeSet
中挑选属性:
TypedArray a = obtainStyledAttributes(attributeSet,
new int[] { android.R.attr.layout_width, android.R.attr.layout_height});
float width = a.getDimension(0, 0f); //第一个参数表示在int数组中的索引,第二个参数为默认值
Log.d(TAG, "typedarray layout_width: " + width);
float height = a.getDimension(1, 0f); //第一个参数表示在int数组中的索引,第二个参数为默认值
Log.d(TAG, "typedarray layout_width: " + height);
可以得到对应的值:
typedarray layout_width: 150.0
typedarray layout_width: 150.0
TypedArray提供了各种Api,如getInteger
,getString
,getDimension
等方法来获取属性值,这些方法都需要传入对应属性名在obtainStyledAttributes
中的int数组的位置索引。
对于TypedArray的分析,这里就只讲从AttributeSet中挑选属性的用法,从defStyleAttr和defStyleRes中挑选属性的用法详见[View绘制体系(四)——TypedArray与自定义属性]。
ps:对于AttributeSet与TypedArray的分析就到这里,后续将会介绍TypedArray与自定义属性的相关内容,敬请关注!