如何创建自定义的Resource实例
由Resource的构造函数Resources(AssetManager assets, DisplayMetrics metrics, Configuration config)了解到,需要获取app外部apk文件资源的Resource对象,首先需要创建对应的AssetManager对象。
public final class AssetManager implements AutoCloseable {
/**
* Create a new AssetManager containing only the basic system assets.
* Applications will not generally use this method, instead retrieving the
* appropriate asset manager with {@link Resources#getAssets}. Not for
* use by applications.
* {@hide}
*/
public AssetManager() {
synchronized (this) {
if (DEBUG_REFS) {
mNumRefs = 0;
incRefsLocked(this.hashCode());
}
init(false);
if (localLOGV) Log.v(TAG, "New asset manager: " + this);
ensureSystemAssets();
}
}
/**
* Add an additional set of assets to the asset manager. This can be
* either a directory or ZIP file. Not for use by applications. Returns
* the cookie of the added asset, or 0 on failure.
* {@hide}
*/
//添加额外的asset路径
public final int addAssetPath(String path) {
synchronized (this) {
int res = addAssetPathNative(path);
if (mStringBlocks != null) {
makeStringBlocks(mStringBlocks);
}
return res;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
所以通过反射可以创建对应的AssertManager,进而创建出对应的Resource实例,代码如下:
private final static Resources loadTheme(String skinPackageName, Context context){
String skinPackagePath = Environment.getExternalStorageDirectory() + "/" + skinPackageName;
File file = new File(skinPackagePath);
Resources skinResource = null;
if (!file.exists()) {
return skinResource;
}
try {
//创建AssetManager实例
AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, skinPackagePath);
//构建皮肤资源Resource实例
Resources superRes = context.getResources();
skinResource = new Resources(assetManager, superRes.getDisplayMetrics(), superRes.getConfiguration());
} catch (Exception e) {
skinResource = null;
}
return skinResource;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
2、如何知道当前属性值在所在Resource中的id
在Resource的源码中,可以发现
public class Resources {
/**
* 通过给的资源名称,类型和包名返回一个资源的标识id。
* @param name 资源的描述名称
* @param defType 资源的类型名称
* @param defPackage 包名
*
* @return 返回资源id,0标识未找到该资源
*/
public int getIdentifier(String name, String defType, String defPackage) {
if (name == null) {
throw new NullPointerException("name is null");
}
try {
return Integer.parseInt(name);
} catch (Exception e) {
// Ignore
}
return mAssets.getResourceIdentifier(name, defType, defPackage);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
也就是说在任意的apk文件中,只需要知道包名(manifest.xml中指定的包名,用于寻找资源和Java类)、资源类型名称、资源描述名称。
比如:在包A中有一个defType为"color",name为color_red_1的属性,通过Resource#getIdentifier则可以获取包B中该名称的颜色资源。
//将skina重View的背景色设置为com.example.skinb中所对应的颜色
if (attrValue.startsWith("@") && attrName.contains("background")){
int resId = Integer.parseInt(attrValue.substring(1));
int originColor = mContext.getResources().getColor(resId);
if (mResource == null){
return originColor;
}
String resName = mContext.getResources(http://www.my516.com).getResourceEntryName(resId);
int skinRealResId = mResource.getIdentifier(resName, "color", "com.example.skinb");
int skinColor = 0;
try{
skinColor = mResource.getColor(skinRealResId);
}catch (Exception e){
Log.e(TAG, "", e);
skinColor = originColor;
}
view.setBackgroundColor(skinColor);
}
---------------------