ArrbuteSet:解析XML。
AttributeSet与attr属性定义以及styleable之间建立键值对联系。
我们通过setContentView以及loadHeaderFromResource或者addPreferenceFromResource等方法添加xml文件,在http://blog.csdn.net/droyon/article/details/22429191博文中我们介绍了layout资源是如何被加载的,以及layout文件中的元素时如何被解析成具体的View,LinearLayout等View对象的。
其中有如下代码:
final AttributeSet attrs = Xml.asAttributeSet(parser);
这里的parser就是XmlResourceParser 。
ArrbuteSet有如下方法:
<View class="android.widget.NumberPicker"
android:id="@+id/bytes"
android:layout_width="48dip"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="16dip"
android:layout_marginRight="16dip"
android:focusable="true"
style="@+id/Theme.Default"
android:focusableInTouchMode="true" />
1、获取特定属性
public String getIdArribute():获取id属性对应的字符串,这里返回@+id/bytes。
pubilc String getClassAttribute():获取class对应的字符串,这里返回android.widget.NumberPicker。
public String getStyleAttribute():获取style对应的字符串,Theme.Default
2、操作通用属性
public int getAttributeCount():获取属性的数目,如上返回10.
3、获取特定类型的值。在View初始化获取xml配置的属性时最为常用。
public XXXType getAttributeXXXTypeValue(int index,XXXType defaultValue)
TypeArray:和AttributeSet进行配合使用,将AttributeSet对象作为参数,TypeArray提供了更为方便的方法获取xml中的配置值。
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View,
defStyle, 0);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background:
background = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.View_padding:
padding = a.getDimensionPixelSize(attr, -1);
break;
TypeArray通过Context对象的obtainStyleAttributes方法获得。第一参数为AttributeSet对象,它包含了xml中配置的所有属性。第二个参数为styleable,aapt将styleable编译成一个数组,该数组包含的内容正是styleable所包含的attr属性的集合。
TypeArray类成员变量如下:
public class TypedArray {
private final Resources mResources;
/*package*/ XmlBlock.Parser mXml;
/*package*/ int[] mRsrcs;
/*package*/ int[] mData;
/*package*/ int[] mIndices;
/*package*/ int mLength;
/*package*/ TypedValue mValue = new TypedValue();
TypedValue包含如下:
/** The type held by this value, as defined by the constants here.
* This tells you how to interpret the other fields in the object. */
public int type;
/** If the value holds a string, this is it. */
public CharSequence string;
/** Basic data in the value, interpreted according to {@link #type} */
public int data;
/** Additional information about where the value came from; only
* set for strings. */
public int assetCookie;
/** If Value came from a resource, this holds the corresponding resource id. */
public int resourceId;
/** If Value came from a resource, these are the configurations for which
* its contents can change. */
public int changingConfigurations = -1;
/**
* If the Value came from a resource, this holds the corresponding pixel density.
* */
public int density;
TypedValue起内部缓冲的作用,mData数组的作用包含了所有指定的styleable属性值。其初始化的地方为:
new TypedArray(this,
new int[len*AssetManager.STYLE_NUM_ENTRIES],
new int[1+len], len);
AssetManager.STYLE_NUM_ENTRIES = 6.也就是说,每一个styleable中包含的attr属性值,其由6个attr属性描述。
/*package*/ static final int STYLE_TYPE = 0;
/*package*/ static final int STYLE_DATA = 1;
/*package*/ static final int STYLE_ASSET_COOKIE = 2;
/*package*/ static final int STYLE_RESOURCE_ID = 3;
/*package*/ static final int STYLE_CHANGING_CONFIGURATIONS = 4;
/*package*/ static final int STYLE_DENSITY = 5;
1、TypeArray使用方法:
TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View,
defStyle, 0);
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_contentDescription:
mContentDescription = a.getString(attr);
break;
2、getString方法流程:
TypeArray.java
public String getString(int index) {
index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return null;
} else if (type == TypedValue.TYPE_STRING) {
return loadStringValueAt(index).toString();
}
TypedValue v = mValue;
if (getValueAt(index, v)) {
Log.w(Resources.TAG, "Converting to string: " + v);
CharSequence cs = v.coerceToString();
return cs != null ? cs.toString() : null;
}
Log.w(Resources.TAG, "getString of bad type: 0x"
+ Integer.toHexString(type));
return null;
}
a、传进来的index乘以6,因为每一个attr由6个int描述它。乘6代表此attr在styleable编译的数组当中的位置。
b、接着获取该attr所对应的类型type。如果type == TypeValue.TYPE_NULL,说明此处无值,直接返回null。如果type类型为TypeValue.TYPE_STRING,则,调用loadStringValueAt方法获取string并返回。
c、如果前两者都不是,那么就从缓冲区TypeValue中进行加载数据。返回结果是调用TypeValue的convertToString方法。其方法实质是强制类型转换,比如获取一个int值,那么就@加上该int值,转换成字符串返回。
getValueAt方法的作用是吧mData中的数据,复制到临时缓冲区v(TypeValue)中。
private boolean getValueAt(int index, TypedValue outValue) {
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return false;
}
outValue.type = type;
outValue.data = data[index+AssetManager.STYLE_DATA];
outValue.assetCookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
outValue.resourceId = data[index+AssetManager.STYLE_RESOURCE_ID];
outValue.changingConfigurations = data[index+AssetManager.STYLE_CHANGING_CONFIGURATIONS];
outValue.density = data[index+AssetManager.STYLE_DENSITY];
outValue.string = (type == TypedValue.TYPE_STRING) ? loadStringValueAt(index) : null;
return true;
}
我们重点看一下第二步中的loadStringValueAt方法。
private CharSequence loadStringValueAt(int index) {
final int[] data = mData;
final int cookie = data[index+AssetManager.STYLE_ASSET_COOKIE];
if (cookie < 0) {
if (mXml != null) {
return mXml.getPooledString(
data[index+AssetManager.STYLE_DATA]);
}
return null;
}
//System.out.println("Getting pooled from: " + v);
return mResources.mAssets.getPooledString(
cookie, data[index+AssetManager.STYLE_DATA]);
}
a、首先获取cookie值,如果cookie小于0,并且mXml对象不为null,则从xml对象中得到String 的值。mXml对象是专门用于解析xml文件的。关于其历程,我们在layout解析历程中有介绍。
其赋值的地方为:
public TypedArray obtainStyledAttributes(AttributeSet set,
int[] attrs, int defStyleAttr, int defStyleRes) {
int len = attrs.length;
TypedArray array = getCachedStyledAttributes(len);
// XXX note that for now we only work with compiled XML files.
// To support generic XML files we will need to manually parse
// out the attributes from the XML file (applying type information
// contained in the resources and such).
XmlBlock.Parser parser = (XmlBlock.Parser)set;
AssetManager.applyStyle(
mTheme, defStyleAttr, defStyleRes,
parser != null ? parser.mParseState : 0, attrs,
array.mData, array.mIndices);
array.mRsrcs = attrs;
array.mXml = parser;
}
如果cookie大于0,则调用mResource.mAssets的getPooledString方法。其意义和mXml差不多。
2、getDrawable方法历程:
public Drawable getDrawable(int index) {
final TypedValue value = mValue;
if (getValueAt(index*AssetManager.STYLE_NUM_ENTRIES, value)) {
if (false) {
System.out.println("******************************************************************");
System.out.println("Got drawable resource: type="
+ value.type
+ " str=" + value.string
+ " int=0x" + Integer.toHexString(value.data)
+ " cookie=" + value.assetCookie);
System.out.println("******************************************************************");
}
return mResources.loadDrawable(value, value.resourceId);
}
return null;
}
调用mResource对象的loadDrawable方法。
/*package*/ Drawable loadDrawable(TypedValue value, int id)
throws NotFoundException {
if (TRACE_FOR_PRELOAD) {
// Log only framework resources
if ((id >>> 24) == 0x1) {
final String name = getResourceName(id);
if (name != null) android.util.Log.d("PreloadDrawable", name);
}
}
final long key = (((long) value.assetCookie) << 32) | value.data;
boolean isColorDrawable = false;
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
isColorDrawable = true;
}
Drawable dr = getCachedDrawable(isColorDrawable ? mColorDrawableCache : mDrawableCache, key);
if (dr != null) {
return dr;
}
Drawable.ConstantState cs = isColorDrawable ? sPreloadedColorDrawables.get(key) : sPreloadedDrawables.get(key);
if (cs != null) {
dr = cs.newDrawable(this);
} else {
if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
value.type <= TypedValue.TYPE_LAST_COLOR_INT) {
dr = new ColorDrawable(value.data);
}
if (dr == null) {
if (value.string == null) {
throw new NotFoundException(
"Resource is not a Drawable (color or path): " + value);
}
String file = value.string.toString();
if (TRACE_FOR_MISS_PRELOAD) {
// Log only framework resources
if ((id >>> 24) == 0x1) {
final String name = getResourceName(id);
if (name != null) android.util.Log.d(TAG, "Loading framework drawable #"
+ Integer.toHexString(id) + ": " + name
+ " at " + file);
}
}
if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie "
+ value.assetCookie + ": " + file);
if (file.endsWith(".xml")) {
try {
XmlResourceParser rp = loadXmlResourceParser(
file, id, value.assetCookie, "drawable");
dr = Drawable.createFromXml(this, rp);
rp.close();
} catch (Exception e) {
NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
} else {
try {
InputStream is = mAssets.openNonAsset(
value.assetCookie, file, AssetManager.ACCESS_STREAMING);
// System.out.println("Opened file " + file + ": " + is);
dr = Drawable.createFromResourceStream(this, value, is,
file, null);
is.close();
// System.out.println("Created stream: " + dr);
} catch (Exception e) {
NotFoundException rnf = new NotFoundException(
"File " + file + " from drawable resource ID #0x"
+ Integer.toHexString(id));
rnf.initCause(e);
throw rnf;
}
}
}
}
if (dr != null) {
dr.setChangingConfigurations(value.changingConfigurations);
cs = dr.getConstantState();
if (cs != null) {
if (mPreloading) {
if (isColorDrawable) {
sPreloadedColorDrawables.put(key, cs);
} else {
sPreloadedDrawables.put(key, cs);
}
} else {
synchronized (mTmpValue) {
//Log.i(TAG, "Saving cached drawable @ #" +
// Integer.toHexString(key.intValue())
// + " in " + this + ": " + cs);
if (isColorDrawable) {
mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
} else {
mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs));
}
}
}
}
}
return dr;
}