引言

在我们的开发中,也许有些项目会有换肤的需求,这个时候会比较头疼怎么做才能做到一键换肤呢?大家肯定是希望只要一行代码就能调用最好。下面我们先分析一下换肤的本质是什么?

原理

换肤,其本质无非就是更换页面元素(view或viewGroup)的属性值,这些属性值都是可以用资源文件表示的,换句话说换肤其实就是替换掉资源文件。比如换个背景,换个文字颜色等。

先看一组QQ换肤:

分析上面的QQ换肤其中的一个页面,白天和夜间风格只有背景颜色、文字颜色、小图标改变了。

再看一组换肤:

分析上面的平板应用换肤其中的一个页面,绿色和蓝色风格只有背景图片、控件颜色改变了。

方案

先看一张控制流程图了解大概思路:

上图大致讲解了换肤的原理,即通过对页面下的所有view重新设置一遍资源文件,而这些资源文件我们可以把它制作成皮肤包(即apk)。

也许通过上面这张流程图你还是不能完全看懂每一个工作流程,下面配合代码详细介绍一下:

  1. 遍历页面下所有元素及其属性集合
    通过在页面(Activity、FragmentActivity)中设置Factory,该Factory能拿到页面下所有viewattrs

    Activity / FragmentActivity

      /*** SkinInflaterFactory是自定义的Factory,实现了android.view.LayoutInflater.Factory* 创建view的事情委托给自定义工厂*/@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);SkinInflaterFactory mSkinInflaterFactory = new SkinInflaterFactory();getLayoutInflater().setFactory(mSkinInflaterFactory);}
    

    SkinInflaterFactory

        /*** SkinInflaterFactory在onCreateView()方法完成了view的创建* 我们可以在该方法中获取view和view的属性集AttributeSet* * @param name view的类名全称 例如:1.android.widget.TextView 2.android.view.View等* @param context 上下文 * @param attrs xml中该view的属性*/@Overridepublic View onCreateView(String name, Context context, AttributeSet attrs) {// 根据name中包含的字段从下列3个包中选择构建出viewView view = null;if (-1 == name.indexOf('.')){if ("View".equals(name)) {view = LayoutInflater.from(context).createView(name, "android.view.", attrs);} if (view == null) {view = LayoutInflater.from(context).createView(name, "android.widget.", attrs);} if (view == null) {view = LayoutInflater.from(context).createView(name, "android.webkit.", attrs);} } else {view = LayoutInflater.from(context).createView(name, null, attrs);}// 解析每个元素下的所有属性(背景、文本颜色、图标等)parseSkinAttr(context, attrs, view)}
    
  2. 解析每个元素下的所有属性(背景、文本颜色、图标等)

      /*** 为方便理解,我拿xml中view的背景属性举例 例如:android:background="@color/color_app_bg"* * 属性名称:background* 属性值:@color/color_app_bg* 资源ID:@color/color_app_bg的ID值,int类型,通过.arsc文件的一套关系映射规则生成* 资源名称:color_app_bg* 资源类型:background*/private void parseSkinAttr(Context context, AttributeSet attrs, View view) {for (int i = 0; i < attrs.getAttributeCount(); i++){// 属性名称String attrName = attrs.getAttributeName(i);// 属性值String attrValue = attrs.getAttributeValue(i);//带@的资源文件if(attrValue.startsWith("@")){try {// 资源IDint id = Integer.parseInt(attrValue.substring(1));// 资源名称String entryName = context.getResources().getResourceEntryName(id);// 资源类型String typeName = context.getResources().getResourceTypeName(id);} catch (NumberFormatException e) {e.printStackTrace();} catch (NotFoundException e) {e.printStackTrace();}}}}
    
  3. 解析每一个attr并将attr的属性名称、资源ID、资源名称、资源类型和对应的view用实体保存起来,以便收到换肤指令时对所有view换肤

  4. 从皮肤包(实际上是另外一个apk)中读取属性值对应的资源文件

       /*** 以下代码可以获取到一个资源ID* 该方法可以获取其他安装包的资源ID,换肤就是利用该方法获取皮肤包的资源文件实现的** @param resName String类型的资源名称* @param defType String类型的资源类型* @param skinPackageName String类型的包名* * @return 返回资源ID*/int resId = mResources.getIdentifier(resName, defType, skinPackageName);
    
  5. 为页面上每一个元素重新设置一遍皮肤包的资源文件

      // 设置背景资源ID(皮肤资源)view.setBackgroundColor(resId);// 设置文本颜色资源ID(皮肤资源)view.setTextColor(resId);// 设置图标资源ID(皮肤资源)view.setImageResource(resId);
    

代码思想:

  1. 封装一个lib,统计页面上所有view和view下attr集
  2. 读取指定apk(皮肤包)的R文件
  3. 将读取到的R文件设置view的每一个attr

以上就是护肤的所有思想啦,如果看到这还没有理解,没关系,我们还可以通过阅读代码来弄清楚它的原理,文末会附上源代码地址。代码整体结构清晰,配合本文阅读代码,相信很快就能理顺。

代码结构

简单介绍下以上皮肤方案的代码结构:app、lib、制作好的皮肤包:

  • app模块------我们的app应用----------------------------------------------com.android.application(主工程)

  • lib模块--------封装换肤功能------------------------------------------------com.android.library(为App模块服务)

  • 皮肤包------放一些需要替换的res文件----------------------------------com.android.application(主工程,打包后放在服务器或其他路径供app取用)

感言

之前我也做过换肤需求,当时也是琢磨了好久,因为需要考虑代码耦合度、扩展性,虽然最终解决了代码结构问题,但还是没能想到 打包成皮肤包 这种方案。这种方案的好处在于只需改动res下部分资源文件,大大节省了时间成本。希望本篇文章能帮助大家快速理解护肤思想,同时也希望大家多分享一些优秀的文章。码字不易,点个赞支持一下吧 hhh~~~

附上开源库链接:Android-Skin-Loader (github)
码云转存链接(解决拉代码慢问题):Android-Skin-Loader (gitee)

后续

如果你使用此方案在实践中遇到问题,可以参考以下这篇文章

Android 换肤方案详解(二)

Android 换肤方案详解(一)相关推荐

  1. Android可更换布局的换肤方案

    换肤,顾名思义,就是对应用中的视觉元素进行更新,呈现新的显示效果.一般来说,换肤的时候只是更新UI上使用的资源,如颜色,图片,字体等等.本文介绍一种笔者自己使用的基于布局的Android换肤方案,不仅 ...

  2. 换肤方案,换肤策略,App插件式换肤实现方案

    UI换皮肤或白天黑夜模式,从产品上来看,是两种不同产品设计模式:白天黑夜模式只有两种模式:而换皮肤可以有多套,可以进行商业化,并盈利. 换肤的本质就是去替换资源文件.我们知道,Android应用程序由 ...

  3. Android 主题切换/换肤方案 研究(四) - qq和qq空间

    4. qq和qq空间 (独立app) 分析时用的是: 1. 夜神android模拟器(因为用android studio自带的模拟器运行x86架构的镜像提示不能安装qq空间,安装arm架构的镜像运行又 ...

  4. Android 应用换肤方案的总结

    虽然现在已经有很多不错的换肤方案,但是这些方案或多或少都存在自己的问题.在这篇文章中,我将对 Android 现有的一些动态换肤方案进行梳理,对其底层实现原理进行分析,然后对开发一个新的换肤方案的可能 ...

  5. 对 Android 应用换肤方案的总结

    作者:me 虽然现在已经有很多不错的换肤方案,但是这些方案或多或少都存在自己的问题.在这篇文章中,我将对 Android 现有的一些动态换肤方案进行梳理,对其底层实现原理进行分析,然后对开发一个新的换 ...

  6. Android面试Hash原理详解二

    Hash系列目录 Android面试Hash原理详解一 Android面试Hash原理详解二 Android面试Hash常见算法 Android面试Hash算法案例 Android面试Hash原理详解 ...

  7. Android 源码编译详解【合集篇】

    Android 源码编译详解[一]:服务器硬件配置及机型推荐 做 Android系统开发多年,开发环境都是入职就搭建好了,入职时拿个账号密码就直接开始搞开发了,年初换了新公司,所有的项目都是刚起步,一 ...

  8. 美团多渠道打包方案详解,速度快到白驹过隙

    美团多渠道打包方案详解,速度快到白驹过隙 Andorid渠道市场有多分散呢?分散到比Android碎片化还严重,你还在为多渠道打包而头疼吗?美团提供了速度快到白驹过隙的多渠道打包方案.说的有点夸张,对 ...

  9. android ------- 开发者的 RxJava 详解

    在正文开始之前的最后,放上 GitHub 链接和引入依赖的 gradle 代码: Github:  https://github.com/ReactiveX/RxJava  https://githu ...

最新文章

  1. 一种在注入进程中使用WTL创建无焦点不在任务栏出现“吸附”窗口的方法和思路
  2. [转]查看事物码相关的数据对象
  3. windows清理图标缓存并重新加载
  4. Linux网络/firewalld和netfilter/netfilter/iptables语法
  5. C#使用iTextSharp操作PDF文件
  6. Sourcetail 一款代码编辑神器,让看源码如丝般顺滑
  7. collector list 多个分组_【S01E07】groupby方法、GroupBy对象、groupby方法的分组键
  8. javascript中实例对象和构造函数关系、原型语法、原型链、call实现继承、apply调用函数、bind拷贝函数、拷贝继承、class类、super、严格模式、高阶函数、闭包、递归、es6简介
  9. (操作系统题目题型总结)第四章:存储管理
  10. 设计模式之:深入浅出 java 单例模式(Singleton)
  11. “碰瓷”特斯拉翻船,卡车界明星创企Nikola身陷“骗局”危机
  12. node.js中实现同步操作的3种实现方法
  13. viewpager初始化fragment没有绘制_NDK OpenGL ES渲染系列 之 绘制三角形
  14. Qt总结之十五:QByteArray详解
  15. 我大意了,刚一放出来就上了牛客网头条了
  16. 23种设计模式(二十)数据结构之迭代器
  17. 关于visual studio和vc版本之间的对应关系(更新至2020.07)
  18. C语言中期报告格式,本科论文中期报告范文_本科毕业论文中期报告模板(2)
  19. 【Oracle】ora-00932:数据类型不一致:应为 -,但却获得BLOB
  20. 美元汇率【贪心算法练习题】

热门文章

  1. 通证经济=区块链技术+商业模式
  2. 三菱fx5U控制三轴伺服定位
  3. vscode代码格式排列_vscode怎么一键规范代码格式
  4. 演讲之禅---- 一位技术演讲家的自白
  5. .com和.cn有什么区别?
  6. 山东大学项目实训——6月29日
  7. 一天的生活不过是一生的缩影罢了
  8. ECOLOGY关闭缓存
  9. 分析与设计建模——类图
  10. Genesis LineHooks特殊指令