android 换肤框架详解1-换肤逻辑基本

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">

    <application
        android: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 = 33

    defaultConfig {
        applicationId = "com.kx.skin"
        minSdk = 28
        targetSdk = 33
        versionCode = 1
        versionName = "1.0"

        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            isMinifyEnabled = false
            proguardFiles(
                getDefaultProguardFile("proguard-android-optimize.txt"),
                "proguard-rules.pro"
            )
        }
    }
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = 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());
              // 创建资源Resources
              AssetManager 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_launcher
        String resName = resources.getResourceEntryName(resId);
        //资源类型类型drawable,color
        String typeName = resources.getResourceTypeName(resId);
        Log.d(TAG, "getThemeResourcesColor resName " + resName);
        Log.d(TAG, "getThemeResourcesColor typeName " + typeName);
        //获取主题资源文件
        Resources newResources = getThemeResources(context);
        //通过资源名称,获取到资源ID
        int 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);