本文章是在内置换肤的基础上进行扩充,内置换肤代码和原理讲解地址:内置换肤原理。如果没有查看这一章节内容,请跳过,本文绝大部分代码是内置换肤代码的扩展。这里只讲解核心代码。

一、动态换肤原理

首先需要明白resources.arsc资源映射表。打包一个apk,点击查看apk,包含如下内容:

这里可以看到resources.arsc,点击查看,如下截图:

左边一栏是资源类型type,右边包含资源id和资源名称name。三者对应关系如下:

  1. 不同的资源类型和资源名称会对应不同的资源id
  2. 相同的资源类型和资源名称对应相同的资源id
  3. 只要资源类型和资源名称有一个不相同,资源id就不同

动态换肤其实也就是根据三者的关系进行了"偷梁换柱",比如我们app中内置有一张图片,对应的资源id为0x7f060000,那么可以根据这个resourceId,获取到图片名称为img,类型为drawable。它是如何匹配皮肤包的相关资源呢?首先,你的皮肤包中必须有相同的图片名称和图片类型,根据图片名称和类型就可以获取到皮肤包的资源id,拿到这个资源id就可以设置图片了。

二、皮肤包

皮肤包其实就是一个apk,只是这个apk只需要配置res目录下的内容和字体样式。需要注意以下几点:

  1. colors.xml:里面的颜色资源要和app名称对应一致
  2. strings.xml:需要配置字体路径,名称要和app对应一致

以下为截图对比:



配置完成以后,只需要生成一下皮肤包apk即可。然后将生成的apk名称修改,这里我改成了qb.skin。然后将它放到SD卡即可。

配置皮肤包需要记住一下几点:

  1. 打出来的皮肤包可删除classes.dex,但至少保留:AndroidManifest.xml、resources.arsc、res/xxx。应该使用解压软件打开删除不需要的即可,不能先解压,删除后再进行压缩。
  2. app内置资源id和皮肤包的资源id不是一定一致的。比如app的drawable中有a.png,b.png,换肤1.png,换肤2.png,对应的资源id分别为0x7f060001,0x7f060002,0x7f060003,0x7f060004。我们皮肤包的drawable中有换肤1.png,换肤2.png,但是它对应资源id分别为0x7f060001,0x7f060002。

三、资源加载

1、初始化app资源appResource、皮肤包资源skinResource,皮肤包包名skinPackageName。主要代码如下:

private SkinManager(Application application) {mApplication = application;//获取当前app的资源appResources = application.getResources();skinCaches = new HashMap<>();}
/*** 1、获取皮肤包资源 skinResources* 2、获取皮肤包包名 skinPackageName** @param skinPath 皮肤包本地路径*/public void loadSkinResources(String skinPath) {if (TextUtils.isEmpty(skinPath)) {Log.e("SkinManager>>>>>>>>>", "加载本地资源");isDefault = true;return;}if (loadSkinCaches(skinPath)) {isDefault = false;Log.e("SkinManager>>>>>>>>>", "缓存找到");return;}/*获取当前app资源管理器 为什么不能使用mApplication.getAssets()因为没有这种方式并没有初始化该类,是没法应用方法反射的*///AssetManager assetManager = mApplication.getAssets();try {AssetManager assetManager = AssetManager.class.newInstance();//获取当前要反射方法Method method = assetManager.getClass().getDeclaredMethod(ADD_ASSET_PATH, String.class);method.setAccessible(true);//执行反射,将皮肤包skin资源加载到当前app资源中method.invoke(assetManager, skinPath);//创建加载皮肤包资源,依然在app应用中加载skinResources = new Resources(assetManager, appResources.getDisplayMetrics(),appResources.getConfiguration());//获取皮肤包包名skinPackageName = mApplication.getPackageManager().getPackageArchiveInfo(skinPath,PackageManager.GET_ACTIVITIES).packageName;//加载成功,设置标识为false,表示可以加载皮肤包资源if (!TextUtils.isEmpty(skinPackageName)) {isDefault = false;//保存到缓存中skinCaches.put(skinPath, new SkinCache(skinPackageName, skinResources));}} catch (Exception e) {e.printStackTrace();isDefault = true;}}

2、根据资源app资源id加载皮肤包资源id。主要代码如下:

*** 获取皮肤包资源id 参考resource.arsc资源映射表** @param resourceId app的资源id* @return 如果找到返回皮肤包资源id,反之返回app的resourceId*/public int loadResouceId(int resourceId) {//没有皮肤包 直接返回app资源idif (isDefault) return resourceId;//获取app资源名称,比如ic_launcher.pngString resourceName = appResources.getResourceEntryName(resourceId);//获取app资源类型 比如drawableString resourceType = appResources.getResourceTypeName(resourceId);/*加载皮肤包的资源id 失败返回0*/int skinResourceId = skinResources.getIdentifier(resourceName, resourceType,skinPackageName);//失败isDefault = trueisDefault = skinResourceId == 0;return isDefault ? resourceId : skinResourceId;}

四、使用

:1、在需要换肤的activity中调用如下方法:

SkinManager.getInstance().loadSkinResources(skinPath);

2、skinPath为皮肤包的路径,通过以下方式获取:

//加载皮肤包skinPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "qb.skin";

3、执行换肤的时候,比如TextView控件,在SkinTextView.class中的换肤方法中,修改之前代码如下:

 public void viewChange() {//改变控件背景颜色int bgKey = R.styleable.SkinTextView[R.styleable.SkinTextView_android_background];int bgColor = skinBean.getResource(bgKey);//动态改变背景颜色if (bgColor > 0) {//是否加载皮肤包成功if(SkinManager.getInstance().isDefault()){// 兼容包转换Drawable drawable = ContextCompat.getDrawable(getContext(), bgColor);// 控件自带api,这里不用setBackgroundColor()因为在9.0测试不通过// setBackgroundDrawable本来过时了,但是兼容包重写了方法setBackgroundDrawable(drawable);}else{Object skinResourceId = SkinManager.getInstance().getBackgroundOrSrc(bgColor);// 兼容包转换if (skinResourceId instanceof Integer) {int color = (int) skinResourceId;setBackgroundColor(color);// setBackgroundResource(color); // 未做兼容测试} else {Drawable drawable = (Drawable) skinResourceId;setBackgroundDrawable(drawable);}}}int textColorKey = R.styleable.SkinTextView[R.styleable.SkinTextView_android_textColor];int textColor = skinBean.getResource(textColorKey);if (textColor > 0) {//是否加载皮肤包成功if(SkinManager.getInstance().isDefault()){ColorStateList color = ContextCompat.getColorStateList(getContext(), textColor);this.setTextColor(color);}else{ColorStateList skinColor = SkinManager.getInstance().getColorStateList(textColor);this.setTextColor(skinColor);}}//字体样式// 根据自定义属性,获取styleable中的字体 custom_typeface 属性int typefaceKey = R.styleable.SkinTextView[R.styleable.SkinTextView_custom_typeface];int textTypefaceResourceId = skinBean.getResource(typefaceKey);if (textTypefaceResourceId > 0) {//是否加载皮肤包成功if (SkinManager.getInstance().isDefault()) {setTypeface(Typeface.DEFAULT);} else {setTypeface(SkinManager.getInstance().getTypeface(textTypefaceResourceId));}}}

其他自定义控件类似相关修改。

4、注意不要忘记在app的配置文件中加上权限

<!--皮肤包权限--><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

最后上一波效果图,字体,图片和自定义控件都换了。是不是6666!!!

动态换肤代码地址如下:

代码地址:app动态换肤

架构师学习--动态换肤相关推荐

  1. C/C++ Linux后台服务器开发高级架构师学习知识点路线总结(2021架构师篇完整版)

    C/C++ Linux后台服务器开发高级架构师学习知识点路线总结(2021架构师篇完整版) 前言: 小编之前有跟大家分享过一篇架构师体系知识点总结的文章,今天在原来的基础上有所改变更新(2021版). ...

  2. C/C++ Linux后台服务器开发高级架构师学习知识点路线总结(2022架构师篇完整版)

    C/C++ Linux后台服务器开发高级架构师学习知识点路线总结(2021架构师篇完整版) 前言: 小编之前有跟大家分享过一篇架构师体系知识点总结的文章,今天在原来的基础上有所改变更新(2021版). ...

  3. 进阶大数据架构师学习路线

    ![在这里插入图片描述](https://img-blog.csdnimg.cn/25b820fe1d054f53bab70310694faffe.jpeg#pic_center 文末有惊喜 大数据架 ...

  4. 网易换肤第三篇:动态换肤实现!

    脑图:https://note.youdao.com/s/DWUvTqgU Demo源码参考:这里. About 网易换肤第一篇:换肤技术解密! 网易换肤第二篇:本地换肤实现! 网易换肤第三篇:动态换 ...

  5. Java 架构师学习路线

    Java 架构师学习路线 一. 框架源码专题 1. 应用框架Spring 1.1. Spring IOC源码剖析 1.2. Spring AOP 源码剖析 1.3. Spring MVC 源码剖析 1 ...

  6. C/C++ Linux 后台服务器开发高级架构师学习知识路(架构师篇)

    @[前言: 小编从事c方面10多年的工作经验.今天跟大家分享一下我总结出来的一系列 C/C Linux后台服务器开发的学习路线.从Linux开发工程师-Linux后台开发工程师-Linux高级互联网架 ...

  7. 关于动态换肤的解决方案

    关于动态换肤的解决方案 最近在学习electron,希望在页面切换的时候动态更换背景色,也是参考了一个知名软件的效果,让我手痒起来,也想实现以下,之前在用element开发的时候接触到过换肤的一些思路 ...

  8. java培训分享:java培训架构师学习线路图

    本期java教程分享主要是讲解关于java培训架构师方面的内容,主要针对java架构师的学习线路图进行一个知识点的概括,下面来看看学习java架构师都需要了解哪些内容吧. java培训分享:java培 ...

  9. 7:MD5、圆形图片、动态换肤

    1.获取一个文件的MD5值(可处理大文件) public static String getSkinMD5(File file) {FileInputStream fis = null;BigInte ...

最新文章

  1. 使用plsql创建定时任务并执行
  2. scatter的用法 随机颜色_plt.scatter画分类月亮湾散点图
  3. 远程桌面与远程协助的区别mstsc /console
  4. java实现把数据写入到Excel并下载
  5. 三星Galaxy S21系列通过认证:终究还是要学苹果“保护环境”?
  6. docker image设置jdk版本_Docker 部署 Spring Boot
  7. Struts(十一):OGNL表达式(二)
  8. 离职 Oracle 首席工程师怒喷:MySQL 是“超烂的数据库”,建议考虑 PostgreSQL
  9. 64位程序core分析
  10. Mac上好用且免费的远程桌面SSH工具(FinalShell)
  11. BackTrack4——利用***测试保证系统安全
  12. azw3转换为pdf_PDF怎么转换为PPT?PDF秒转PPT秘技!
  13. 建筑智能化资质办理条件
  14. logistic逻辑回归
  15. hadoop系列-hadoop版本选择
  16. GIS空间服务赋能邵阳不动产登记可视化办理
  17. 高中数学怎么学好学好高中数学的技巧
  18. Android 虚拟机EditText键盘无法输入解决方法
  19. 2014.7.8 第一天学习hfss13
  20. 总结几点学习模电难的原因

热门文章

  1. 复产复工后,这些消防安全知识你知道几条
  2. 属马的人取名不宜用字
  3. Swift的基本使用介绍
  4. 【Vue笔记】Vue生命周期函数详细图解
  5. Windows下nginx配置python服务器
  6. URL、域名、DNS
  7. 具体代码学习C++(03)--常量
  8. csdn一个神奇的网站
  9. PHP与出库单打印预览,包含下载与打印功能
  10. 利用微软小冰 API 接口,对大量照片进行颜值评分