android 换肤框架详解1-换肤逻辑基本
- 换肤框架流程
1,通过AssetManager获取换肤的资源文件
2,通过原文件中的resId获取到res名称和res类型,比如resId未R.color.red,这里的名称就是red,类型就是color
3,在换肤的资源文件AssetManager中,用原文件的resId的res名称和res类型获取到换肤资源文件中的ResId,在通过AssetManager.getXXX拿到对应的资源
4,将拿到的资源文件部署到View
- 代码模块
1,创建资源包
由于资源包只需要里面的资源文件,多余的内容都可以删除到,这里只留如下内容
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"><applicationandroid:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:roundIcon="@mipmap/ic_launcher_round"android:supportsRtl="true" /></manifest>
gradle中不需要其他的引用包
build.gradle.kts
plugins {id("com.android.application")
}android {namespace = "com.kx.skin"compileSdk = 33defaultConfig {applicationId = "com.kx.skin"minSdk = 28targetSdk = 33versionCode = 1versionName = "1.0"testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"}buildTypes {release {isMinifyEnabled = falseproguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"),"proguard-rules.pro")}}compileOptions {sourceCompatibility = JavaVersion.VERSION_1_8targetCompatibility = JavaVersion.VERSION_1_8}
}dependencies {
}
在原本的app和换肤资源的app中同时创建名称相同的资源
将编译好的skin apk放到assets目录下
- 创建资源文件的AssetManager
public static Resources getThemeResources(Context context) {try {//通过反射创建AssetManager
// AssetManager assetManager = AssetManager.class.newInstance();
// Method add = assetManager.getClass().getDeclaredMethod("addAssetPath", String.class);//通过反射加载路径
// int cookie = (int) add.invoke(assetManager, "/sdcard/skin/skin.skin");//将assets中的文件拷贝到自己私有的文件中File skinFile = copyAssetToFiles(context,"skin-debug.apk", // assets 下的相对路径"skin-debug.apk"); // 目标文件名boolean exists = skinFile.exists(); // true 表示存在Log.e(TAG, "加载文件 exists " + exists + " getAbsolutePath " + skinFile.getAbsolutePath());// 创建资源ResourcesAssetManager assetManager = new AssetManager();int cookie = assetManager.addAssetPath(skinFile.getAbsolutePath());if (cookie == 0) {Log.e(TAG, "加载失败,路径无效或权限不足");}Resources oldRes = context.getResources();Resources newRes = new Resources(assetManager,oldRes.getDisplayMetrics(),oldRes.getConfiguration());return newRes;} catch (Throwable e) {e.printStackTrace();Log.d(TAG, "Throwable " + e);}return null;}
2,在换肤的资源文件AssetManager中,用原文件的resId的res名称和res类型获取到换肤资源文件中的ResId,在通过AssetManager.getXXX拿到对应的资源
public static int getThemeResourcesColorResId(Context context, int resId) {Log.d(TAG, "getThemeResourcesColor resId " + resId);Resources resources = context.getResources();//包名:资源类型/资源名
// String resName = resources.getResourceName(resId);//返回格式:ic_launcher(仅资源名)R.drawable.ic_launcher,R.color.ic_launcherString resName = resources.getResourceEntryName(resId);//资源类型类型drawable,colorString typeName = resources.getResourceTypeName(resId);Log.d(TAG, "getThemeResourcesColor resName " + resName);Log.d(TAG, "getThemeResourcesColor typeName " + typeName);//获取主题资源文件Resources newResources = getThemeResources(context);//通过资源名称,获取到资源IDint newResId = newResources.getIdentifier(resName, // 资源名typeName, // 资源类型"com.kx.skin" // 应用包名);Log.d(TAG, "getThemeResourcesColor newResId " + newResId);//通过资源ID,获取到对应资源的值int newColorResId = newResources.getColor(newResId, null);Log.d(TAG, "getThemeResourcesColor newColorResId " + newColorResId);return newColorResId;}
代码调用
int resId = ThemeModeChange.getThemeResourcesColorResId(ThemeActivity.this,R.color.content);tv_content.setBackgroundColor(resId);