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);
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类成员变量如下:
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();
/** 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;
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差不多。
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; }