Android换肤系列 Resources
2015-11-08 13:26
896 查看
在Android中Resources类用于获取应用资源(如:图片、颜色、文本),并自动根据地区、语言、分辨率、屏幕方向等获取对应的资源。以下api doc上对Resources类的介绍:
从上面介绍中我们可以知道,Resources基于AssetManager,资源请求是通过AssetManager类来完成,而java层的AssetManager最终则是通过C++层AssetManager类来完成arsc文件解析,资源的加载,机型适配等。深入介绍移步Android资源管理框架(Asset Manager)简要介绍和学习计划。Resources类中提供了一些public的api接口用于获取应用资源,如:getColor、 getDrawable、 getString,通过这些接口我们能获取指定资源颜色、背景图片、文本等,接下来将详细分析getColor和getDrawable的过程。
在Activity中我们可以使用getResources().getColor()获取指定资源color,下图getColor的过程。(基于android 4.4源码)
Step 1. Resources.getColor
方法里面首先判断缓存mTmpValue是否为空,为空的话就创建出一个新的TypedValue,mTmpValue变量的并发访问则通过mAccessLock来保护。接着调用另一个方法getValue。
Step 2. Resources.getValue
getValue方法内部则是通过调用AssetManager的getResourceValue方法来获取资源id对应的color,返回值资源项的信息保存在outValue中。对应getResourceValue的底层实现这里不再深究,Android资源管理框架(Asset Manager)简要介绍和学习计划 有详细介绍。
Step 3. Resources.loadColorStateList
根据在上一步骤中取得的color资源进行判断,如果是普通的color类型(xml文件中定义的颜色,这里包括定义在color.xml和在xml中直接使用色值),则直接返回给调用者;否则通过loadColorStateList来加载定义在xml文件中的selector资源选择器。
方法首先从缓存中判断资源是否加载过,若加载过则直接返回。接着判断索引文件中资源id对应的资源项的文件名称是否以.xml结尾,不是的话则直接抛出NotFoundException异常此次查找结束;确定是xml文件后,通过loadXmlResourceParser方法将xml文件加载进来,接着调用ColorStateList.createFromXml解析出ColorStateList对象。
Step 4. ColorStateList.createFromXml
createFromXml方法就只是简单的遍历获取第一个xml节点,然后接着调用createFromXmlInner方法
createFromXmlInner方法中首先判断节点类型是否为”selector”,否则异常结束,接着创建ColorStateList对象,调用对象的inflate方法解析xml文件。
inflate首先遍历所有的item节点, 然后判断item节点里面的color属性是值类型(直接在xml文件中使用”#RRGGBB”)还是引用类型(通过@color引用color.xm文件内容)。如果是值类型,通过getAttributeIntValue方法获取xml文件中指定的color;否则需要根据xml文件中引用资源的id再次调用Resources.getColor获取资源。从这里也可以看出虽然color selector支持嵌套,但是引用xml资源的item项获取的是默认的color。
接下来是getDrawable的过程
从图中可以看出getDrawable和getColor前几个步骤是相似的,都是是先通过资源id从索引文件中获取到相应资源项的信息,步骤1和2参考getColor。
Step 3. Resources.loadDrawable
方法里面首先判断资源的类型是否为color,代码里面根据isColorDrawable变量来区分两种资源的缓存:color保存在mColorDrawableCache、drawable保存在mDrawableCache。这里假设id对应的资源在缓存里面不存在,如果资源的类型为color则直接创建一个新的ColorDrawable对象,资源内容就是上一个步骤getValue从索引文件解析得到的保存在valueType的data中。如果是文件类型则判断是否为xml文件,这里通过Drawable.createFromXml来解析资源xml文件。Drawable.createFromXml 方法和geColor的ColorStateList.createFromXml是相同的。
Step 4. Drawable.createFromXmlInner
createFromXmlInner方法内通过判断item的名称选择不同的Drawable实现,有StateListDrawable,LevelListDrawable,ColorDrawable,BitmapDrawable等14中资源类型,这里以StateListDrawable为例。当选择Drawable的具体实现后,接着调用inflate方法来解析xml文件。
inflate首先遍历所有的item节点,从drawable属性中读取引用的资源id存入drawableRes变量,接着再调用Resources的getDrawable方法获取drawableRes资源id对应的drawable资源。
从上面我们知道不管是getColor还是getDrawable方法,都是先调用getValue方法从资源索引文件中获取资源id对应的资源项的信息,接着再根据资源类型调用不同的drawable进行资源对象的创建。这里比较特殊的是color资源,在调用getValue方法的后,如果是color资源,color的值是保存在outValue变量中。如果方法调用的是getColor则返回outValue中data的值,如果是getDrawable则通过outValue中data的值创建一个ColorDrawable对象。
从上面介绍中我们可以知道,Resources基于AssetManager,资源请求是通过AssetManager类来完成,而java层的AssetManager最终则是通过C++层AssetManager类来完成arsc文件解析,资源的加载,机型适配等。深入介绍移步Android资源管理框架(Asset Manager)简要介绍和学习计划。Resources类中提供了一些public的api接口用于获取应用资源,如:getColor、 getDrawable、 getString,通过这些接口我们能获取指定资源颜色、背景图片、文本等,接下来将详细分析getColor和getDrawable的过程。
在Activity中我们可以使用getResources().getColor()获取指定资源color,下图getColor的过程。(基于android 4.4源码)
Step 1. Resources.getColor
public int getColor(int id) throws NotFoundException { TypedValue value; synchronized (mAccessLock) { value = mTmpValue; if (value == null) { value = new TypedValue(); } getValue(id, value, true); if (value.type >= TypedValue.TYPE_FIRST_INT && value.type <= TypedValue.TYPE_LAST_INT) { mTmpValue = value; return value.data; } else if (value.type != TypedValue.TYPE_STRING) { throw new NotFoundException( "Resource ID #0x" + Integer.toHexString(id) + " type #0x" + Integer.toHexString(value.type) + " is not valid"); } mTmpValue = null; } ColorStateList csl = loadColorStateList(value, id); synchronized (mAccessLock) { if (mTmpValue == null) { mTmpValue = value; } } return csl.getDefaultColor(); }
方法里面首先判断缓存mTmpValue是否为空,为空的话就创建出一个新的TypedValue,mTmpValue变量的并发访问则通过mAccessLock来保护。接着调用另一个方法getValue。
Step 2. Resources.getValue
public void getValue(int id, TypedValue outValue, boolean resolveRefs) throws NotFoundException { boolean found = mAssets.getResourceValue(id, 0, outValue, resolveRefs); if (found) { return; } throw new NotFoundException("Resource ID #0x" + Integer.toHexString(id)); }
getValue方法内部则是通过调用AssetManager的getResourceValue方法来获取资源id对应的color,返回值资源项的信息保存在outValue中。对应getResourceValue的底层实现这里不再深究,Android资源管理框架(Asset Manager)简要介绍和学习计划 有详细介绍。
Step 3. Resources.loadColorStateList
根据在上一步骤中取得的color资源进行判断,如果是普通的color类型(xml文件中定义的颜色,这里包括定义在color.xml和在xml中直接使用色值),则直接返回给调用者;否则通过loadColorStateList来加载定义在xml文件中的selector资源选择器。
/*package*/ ColorStateList loadColorStateList(TypedValue value, int id) throws NotFoundException { ... ColorStateList csl; csl = getCachedColorStateList(key); if (csl != null) { return csl; } csl = sPreloadedColorStateLists.get(key); if (csl != null) { return csl; } String file = value.string.toString(); if (file.endsWith(".xml")) { try { XmlResourceParser rp = loadXmlResourceParser( file, id, value.assetCookie, "colorstatelist"); csl = ColorStateList.createFromXml(this, rp); rp.close(); } catch (Exception e) { throw rnf; } } .... if (csl != null) { if (mPreloading) { if (verifyPreloadConfig(value.changingConfigurations, 0, value.resourceId, "color")) { sPreloadedColorStateLists.put(key, csl); } } else { synchronized (mAccessLock) { mColorStateListCache.put(key, new WeakReference<ColorStateList>(csl)); } } } return csl; }
方法首先从缓存中判断资源是否加载过,若加载过则直接返回。接着判断索引文件中资源id对应的资源项的文件名称是否以.xml结尾,不是的话则直接抛出NotFoundException异常此次查找结束;确定是xml文件后,通过loadXmlResourceParser方法将xml文件加载进来,接着调用ColorStateList.createFromXml解析出ColorStateList对象。
Step 4. ColorStateList.createFromXml
public static ColorStateList createFromXml(Resources r, XmlPullParser parser) throws XmlPullParserException, IOException { AttributeSet attrs = Xml.asAttributeSet(parser); int type; while ((type=parser.next()) != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT) { } if (type != XmlPullParser.START_TAG) { throw new XmlPullParserException("No start tag found"); } return createFromXmlInner(r, parser, attrs); }
createFromXml方法就只是简单的遍历获取第一个xml节点,然后接着调用createFromXmlInner方法
private static ColorStateList createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { ColorStateList colorStateList; final String name = parser.getName(); if (name.equals("selector")) { colorStateList = new ColorStateList(); } else { throw new XmlPullParserException( parser.getPositionDescription() + ": invalid drawable tag " + name); } colorStateList.inflate(r, parser, attrs); return colorStateList; }
createFromXmlInner方法中首先判断节点类型是否为”selector”,否则异常结束,接着创建ColorStateList对象,调用对象的inflate方法解析xml文件。
private void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { ... while ((type=parser.next()) != XmlPullParser.END_DOCUMENT && ((depth=parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { .... final int numAttrs = attrs.getAttributeCount(); int[] stateSpec = new int[numAttrs]; for (i = 0; i < numAttrs; i++) { final int stateResId = attrs.getAttributeNameResource(i); if (stateResId == 0) break; if (stateResId == com.android.internal.R.attr.color) { colorRes = attrs.getAttributeResourceValue(i, 0); if (colorRes == 0) { color = attrs.getAttributeIntValue(i, color); haveColor = true; } } .... } ... if (colorRes != 0) { color = r.getColor(colorRes); } .... } .... }
inflate首先遍历所有的item节点, 然后判断item节点里面的color属性是值类型(直接在xml文件中使用”#RRGGBB”)还是引用类型(通过@color引用color.xm文件内容)。如果是值类型,通过getAttributeIntValue方法获取xml文件中指定的color;否则需要根据xml文件中引用资源的id再次调用Resources.getColor获取资源。从这里也可以看出虽然color selector支持嵌套,但是引用xml资源的item项获取的是默认的color。
接下来是getDrawable的过程
从图中可以看出getDrawable和getColor前几个步骤是相似的,都是是先通过资源id从索引文件中获取到相应资源项的信息,步骤1和2参考getColor。
Step 3. Resources.loadDrawable
/*package*/ Drawable loadDrawable(TypedValue value, int id) throws NotFoundException { boolean isColorDrawable = false; if (value.type >= TypedValue.TYPE_FIRST_COLOR_INT && value.type <= TypedValue.TYPE_LAST_COLOR_INT) { isColorDrawable = true; } .... Drawable.ConstantState cs; if (isColorDrawable) { cs = sPreloadedColorDrawables.get(key); } else { cs = sPreloadedDrawables[mConfiguration.getLayoutDirection()].get(key); } if (cs != null) { dr = cs.newDrawable(this); } else { if (isColorDrawable) { dr = new ColorDrawable(value.data); } if (dr == null) { String file = value.string.toString(); if (DEBUG_LOAD) Log.v(TAG, "Loading drawable for cookie " + value.assetCookie + ": " + file); if (file.endsWith(".xml")) { Trace.traceBegin(Trace.TRACE_TAG_RESOURCES, file); try { XmlResourceParser rp = loadXmlResourceParser( file, id, value.assetCookie, "drawable"); dr = Drawable.createFromXml(this, rp); rp.close(); } catch (Exception e) { } } else { try { InputStream is = mAssets.openNonAsset( value.assetCookie, file, AssetManager.ACCESS_STREAMING); dr = Drawable.createFromResourceStream(this, value, is, file, null); is.close(); } catch (Exception e) { } } } } if (dr != null) { dr.setChangingConfigurations(value.changingConfigurations); cs = dr.getConstantState(); if (cs != null) { if (mPreloading) { .... } else { synchronized (mAccessLock) { if (isColorDrawable) { mColorDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); } else { mDrawableCache.put(key, new WeakReference<Drawable.ConstantState>(cs)); } } } } } return dr; }
方法里面首先判断资源的类型是否为color,代码里面根据isColorDrawable变量来区分两种资源的缓存:color保存在mColorDrawableCache、drawable保存在mDrawableCache。这里假设id对应的资源在缓存里面不存在,如果资源的类型为color则直接创建一个新的ColorDrawable对象,资源内容就是上一个步骤getValue从索引文件解析得到的保存在valueType的data中。如果是文件类型则判断是否为xml文件,这里通过Drawable.createFromXml来解析资源xml文件。Drawable.createFromXml 方法和geColor的ColorStateList.createFromXml是相同的。
Step 4. Drawable.createFromXmlInner
public static Drawable createFromXmlInner(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { Drawable drawable; final String name = parser.getName(); if (name.equals("selector")) { drawable = new StateListDrawable(); } else if (name.equals("level-list")) { .... } else { throw new XmlPullParserException(parser.getPositionDescription() + ": invalid drawable tag " + name); } drawable.inflate(r, parser, attrs); return drawable;
createFromXmlInner方法内通过判断item的名称选择不同的Drawable实现,有StateListDrawable,LevelListDrawable,ColorDrawable,BitmapDrawable等14中资源类型,这里以StateListDrawable为例。当选择Drawable的具体实现后,接着调用inflate方法来解析xml文件。
public void inflate(Resources r, XmlPullParser parser, AttributeSet attrs) throws XmlPullParserException, IOException { TypedArray a = r.obtainAttributes(attrs, com.android.internal.R.styleable.StateListDrawable); .... while ((type = parser.next()) != XmlPullParser.END_DOCUMENT && ((depth = parser.getDepth()) >= innerDepth || type != XmlPullParser.END_TAG)) { .... for (i = 0; i < numAttrs; i++) { final int stateResId = attrs.getAttributeNameResource(i); if (stateResId == 0) break; if (stateResId == com.android.internal.R.attr.drawable) { drawableRes = attrs.getAttributeResourceValue(i, 0); } .... } Drawable dr; if (drawableRes != 0) { dr = r.getDrawable(drawableRes); } .... } }
inflate首先遍历所有的item节点,从drawable属性中读取引用的资源id存入drawableRes变量,接着再调用Resources的getDrawable方法获取drawableRes资源id对应的drawable资源。
从上面我们知道不管是getColor还是getDrawable方法,都是先调用getValue方法从资源索引文件中获取资源id对应的资源项的信息,接着再根据资源类型调用不同的drawable进行资源对象的创建。这里比较特殊的是color资源,在调用getValue方法的后,如果是color资源,color的值是保存在outValue变量中。如果方法调用的是getColor则返回outValue中data的值,如果是getDrawable则通过outValue中data的值创建一个ColorDrawable对象。
相关文章推荐
- Android Design Support Library 八个强大功能的控件
- Android性能测试之耗电量测试
- Android——自定义AlertDialog
- android 项目中引入2个v4包报错
- android 引入ActionBar报错Caused by: java.lang.IllegalStateException: You need to use a Theme.AppCompat t
- android倒计时功能的实现(CountDownTimer)
- Android 依据变量来获得资源R中的id值
- Android自定义View系列之动态变化的Button
- Android笔记—应用更新
- Android最方便的数据库--LitePal
- Android Error retrieving parent for item: No resource found that matches the given name '...'
- AndroidStudio使用问题汇总——导入(import)工程时常见错误
- Android——SQLite使用
- Android设置launchMode为singleTask的Activity怎么刷新页面内容
- 如何检查你的安卓设备是否易受攻击?
- 对android消息机制的总结
- Android 学习笔记之界面布局
- Android 学习笔记之界面布局
- Volley学习小结
- Layoutinflater用法