android 換膚框架詳解1-換膚邏輯基本-CSDN博客
android 換膚框架詳解2-LayoutInflater源碼解析-CSDN博客
android 換膚框架詳解3-自動換膚原理梳理-CSDN博客
- 換膚框架流程
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);