简介
app换肤,就是将我们设置的对应资源文件,比如drawable,style,textSize等,替换成资源包里面的数据
这里我们将以一个简单的替换drawable资源文件,来讲解其原理
先来开一个方法,当我们调用getMyResource(R.mipmap.ic_launcher)时候
private int getMyResource(int resId) {
Resources resources = getResources();
String resourceEntryName = resources.getResourceEntryName(resId);//ic_launcher
Log.d(TAG, "resourceEntryName: " + resourceEntryName);
String resourcePackageName = resources.getResourcePackageName(resId);//com.example.invoke
Log.d(TAG, "resourcePackageName: " + resourcePackageName);
String resourceTypeName = resources.getResourceTypeName(resId);//mipmap
Log.d(TAG, "resourceTypeName: " + resourceTypeName);
String resourceName = resources.getResourceName(resId);//com.example.invoke:mipmap/ic_launcher
Log.d(TAG, "resourceName: " + resourceName);
int identifier = resources.getIdentifier(resourceEntryName, resourceTypeName, resourcePackageName); //ic_launcher mipmap com.example.invoke
Log.d(TAG, "identifier: " + identifier);
return identifier;
}
所得到的log日志
com.example.invoke D/MainActivity: resourceEntryName: ic_launcher
com.example.invoke D/MainActivity: resourcePackageName: com.example.invoke
com.example.invoke D/MainActivity: resourceTypeName: mipmap
com.example.invoke D/MainActivity: resourceName: com.example.invoke:mipmap/ic_launcher
com.example.invoke D/MainActivity: identifier: 2131361792
在对应的R文件里面,我们可以找到与相 2131361792 对应的资源
换肤方法
我们先看一下主要方法getIdentifier,其中传入了三个参数name(ic_launcher) ,defType(mipmap ),以及defPackage(com.example.invoke),看到这里就看到了解决方式,我们只需要将defPackage修改成资源包(com.zkq.app_skin)的包名,通过getIdentifier获得资源包(com.zkq.app_skin)的资源ID,反相获得相应资源包的R.mipmap.ic_launcher文件,就可以达到换肤的目的
public int getIdentifier(String name, String defType, String defPackage) {
return mResourcesImpl.getIdentifier(name, defType, defPackage);
}
步骤
了解了实现方式,现在正是开始实现代码
先看主项目(com.example.invoke)结构,我们需要换肤,修改test.png文件
资源文件(com.zkq.app_skin)的结构,同样的名字,存放规则,我们将资源文件生成的apk存放到sdcard中,命名为app_skin-debug.apk,并开启读取sdcard权限。当然一般资源包都是通过http请求,从服务器下载,并验证MD5等,我们这里主要讲解原理,就不实现了
在主项目中
- 获取资源文件路径
String path = Environment.getExternalStorageDirectory() + File.separator + "app_skin-debug.apk";
- 获取资源文件的skinResources
//反射创建AssetManager
AssetManager manager = AssetManager.class.newInstance();
// 资料路径设置 目录或者压缩包
Method addAssetPath = manager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(manager, path);
Resources appResources = getResources();
Resources skinResources = new Resources(manager, appResources.getDisplayMetrics(), appResources.getConfiguration());
- 获得资源文件的包名
//获取外部Apk(皮肤包) 包名
PackageManager packageManager = getPackageManager();
PackageInfo packageArchiveInfo = packageManager.getPackageArchiveInfo(path, PackageManager.GET_ACTIVITIES);
String packageName = packageArchiveInfo.packageName;
- 通过资源包名,获得资源包相应资源在R文件中的id值
/**
* 获取相应报名下面的资源文件
* @return
*/
private int getMyResource(Resources skinResources, int resId,String packagePath) {
Log.e(TAG, "---------------------------------------");
String resName =getResources().getResourceEntryName(resId);//test /colorPrimaryDark
String resType =getResources().getResourceTypeName(resId);//mipmap
int identifier = skinResources.getIdentifier(resName, resType, packagePath); //test mipmap com.example.invoke
Log.d(TAG, "identifier: " + identifier);
return identifier;
}
- 获得资源文件的R.mipmap.test文件的drawable
int myResource = getMyResource(skinResources, R.mipmap.test, packageName);
Drawable drawable = skinResources.getDrawable(myResource);
- 给imageview设置相应的drawable资源
ImageView imageview = findViewById(R.id.imageview);
imageview.setImageDrawable(drawable);
到这里,我们修改R.mipmap.test的资源,就算完成了
总结
注意事项
- 资源文件名字,属性必须一直
- 需要打开sdcard存储权
demo下载地址使用demo时候,需要将app_skin中生成的apk考入到sdcard中