一、原理

普通的开发时,如果要给一个View设置背景颜色,通常会这样

view.setBackgroundColor(context.getResources().getColor(R.color.black));

context.getResources()返回一个Resources对象,里面有getColor(...),getString(...)等方法,可以通过这些方法返回颜色字符串等。那么,如果我们如果能从另一个地方获取Resources对象,例如一个皮肤包,然后读取里面的颜色图片等,是不是就可以换肤了呢。

二、如何获取皮肤包的资源文件

AssetManager提供了这么一个方法,通过path更改Asset路径.该方法是hide的。

    public int addAssetPath(String path) {return addAssetPathInternal(path, false /*overlay*/, false /*appAsLib*/);}

然后Resources的构造方法是这样的:

    public Resources(AssetManager assets, DisplayMetrics metrics, Configuration config) {this(null);mResourcesImpl = new ResourcesImpl(assets, metrics, config, new DisplayAdjustments());}

所以,想要通过皮肤包创建一个Resources,需要这样写:

 private Resources getAsseResources(Context context, String skinApkPath) {try {Method method = AssetManager.class.getDeclaredMethod("addAssetPath", String.class);AssetManager assetManager = AssetManager.class.newInstance();method.invoke(assetManager, skinApkPath);return new Resources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());} catch (Exception e) {e.printStackTrace();} return null;}

三、简单的demo

首先,创建一个Hello World!工程,包名为MyAppliaction,这时MainActivity应该有一个hello world的textview
然后再res/values文件下创建一个colors.xml文件,里面添加一个颜色,我这里取名text_color,颜色为红色

<?xml version="1.0" encoding="utf-8"?>
<resources><color name="text_color">#f00</color>
</resources>

再在MainActivity中这个textview设置为text_color的颜色

textView.setTextColor(getResources().getColor(R.color.text_color));

运行后,出现红色的hello world

然后,我们来创建皮肤包
创建一个空的工程,直接命名为MyAppliaction2

同样res/values文件夹下创建colors.xml文件,把颜色改为绿色,name还是text_color不变

<?xml version="1.0" encoding="utf-8"?>
<resources><color name="text_color">#0f0</color>
</resources>

将这个MyAppliaction2项目打包生成apk。这就是一个皮肤包了。然后不用安装,直接放到手机目录。为了省事,我直接放到外部存储的根目录。

如上图,app-debug.apk即为皮肤包。
注意,如果皮肤包放在外部存储,主应用(此文为MyAppliaction)一定要添加读写SD卡的权限,并赋予权限。
皮肤包做成了,如何使用这个皮肤包呢。
创建一个工具类SkinUtil.java

public class SkinUtil {private static SkinUtil skinUtil;private Resources mResources;private String mPath;private Context mContext;private String skinPackageName;private SkinUtil(Context context) {this.mPath = Environment.getExternalStorageDirectory().getAbsolutePath() + "/app-debug.apk";//皮肤包路径。如果放在外部存储,一定要添加外部存储的读写权限this.mResources = getAsseResources(context, mPath);skinPackageName = getApkInfo(context,mPath);}public static SkinUtil getInstance(Context context) {if (skinUtil == null) {skinUtil = new SkinUtil(context);}skinUtil.mContext = context;return skinUtil;}//获取皮肤包的Resourcesprivate Resources getAsseResources(Context context, String skinApkPath) {try {Method method = AssetManager.class.getDeclaredMethod("addAssetPath", String.class);AssetManager assetManager = AssetManager.class.newInstance();method.invoke(assetManager, skinApkPath);return new Resources(assetManager,context.getResources().getDisplayMetrics(),context.getResources().getConfiguration());} catch (Exception e) {e.printStackTrace();}return null;}public int getColor(int id) {//通过本应用R文件的id获取nameString name = mContext.getResources().getResourceEntryName(id);//通过name获取其在皮肤包的R文件中的idint identifier = mResources.getIdentifier(name, "color", skinPackageName);if (identifier == 0) {//如果没有查到,返回当前的应用的颜色return mContext.getResources().getColor(id);}//根据皮肤包的R文件中的id获取颜色return mResources.getColor(identifier);}public Drawable getDrawable(int id) {//通过本应用R文件的id获取nameString name = mContext.getResources().getResourceEntryName(id);//通过name获取其在皮肤包的R文件中的idint identifier = mResources.getIdentifier(name, "drawable", skinPackageName);if (identifier == 0) {//如果没有查到,返回当前的应用的颜色
//            return mContext.getResources().getColor(id);return null;}//根据皮肤包的R文件中的id获取颜色
//        return mResources.getColor(identifier);return mResources.getDrawable(identifier);}//通过文件路径解析包名public  String getApkInfo(Context context, String apkPath) {try {PackageManager pm = context.getPackageManager();PackageInfo info = pm.getPackageArchiveInfo(apkPath, PackageManager.GET_ACTIVITIES);if (info != null) {ApplicationInfo appInfo = info.applicationInfo;return appInfo.packageName;  //得到安装包名称}} catch (Exception e) {e.printStackTrace();}return null;}

getAsseResources方法就是前文提到的获取皮肤包的资源文件,getColor方法就是获取皮肤包的color,返回一个color的16进制值。然后通过setTextColor设置这个值就可以了。把前文的setTextColor修改成使用SkinUtil的getcolor方法。

textView.setTextColor(SkinUtil.getInstance(this).getColor(R.color.text_color));


ok,hello world变成了皮肤包的绿色的了。

这里介绍了getcolor的方法,如果是drawable、string原理都是的一样的,通先获取当前资源id对应的name,然后用这个name获取其在皮肤包中的R文件对应的id,然后再返回具体的颜色图片的。

这里有个缺陷,不能处理setContentView(R.layout.XXX)的颜色,而且设置颜色需要把Context.getResources.get...换成SkinUtil.getInstance(context).get..,如果是开发初还可以依次改,如果是一个开发了很久的项目,改的地方太多了。为了解决这个问题,下一篇讲如何用hook修改全局属性。

android 换肤探索(一) 手把手做一个皮肤包相关推荐

  1. Android换肤:从外存中读取皮肤包资源实现换肤

    效果图: 简介 经过上篇的介绍(https://editor.csdn.net/md/?articleId=106350257),其实换肤的操作十分简单,简单的说就是A模式调用A资源文件,B模式调用B ...

  2. Android换肤总结

    文章目录 换肤方案 Theme换肤 Resouce换肤 2.拿到皮肤包Resource对象 3.标记需要换肤的View 4.缓存需要换肤的View 5.切换时即时刷新页面 6.制作皮肤包 UiMode ...

  3. Android 换肤方案详解(一)

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

  4. Android换肤技术

    所谓换肤技术,就是用户可以根据自己的喜好,选择自己喜欢的并且APP提供的颜色,背景图片,作为整个app的主题背景颜色,或者字体颜色等等...满足用户的需求. APP换肤 主要分为2种: 1.内置换肤: ...

  5. android换肤哪个简单,Android换肤

    这是一个Android换肤的库,代码量极少,支持换肤的情况还比较多,提供了以下功能: 无需重启,一键换肤效率高 支持App内多套皮肤换肤 支持插件式动态换肤 支持Activity,Fragment,以 ...

  6. android换肤的实现方案,Android应用开发之Android一键换肤功能实现

    本文将带你了解Android应用开发之Android一键换肤功能实现,希望本文对大家学Android有所帮助. < 市面上对数的App都提供换肤功能,这里暂且不讲白天和夜间模式 下图是网易云音乐 ...

  7. Android 换肤功能的实现(Apk插件方式)

    一.概述 由于Android 没有提供一套统一的换肤机制,我猜可能是因为国外更注重功能和体验的原因 所以国内如果要做一个漂亮的换肤方案,需要自己去实现. 目前换肤的方法大概有三种方案: (1)把皮肤资 ...

  8. Android换肤功能实现与换肤框架QSkinLoader使用方式介绍

    框架地址:https://github.com/qqliu10u/QSkinLoader 效果图 https://github.com/qqliu10u/QSkinLoader/raw/master/ ...

  9. Android 换肤之旅——主题切换

    随着手机应用的成熟发展,市面上的应用已不在以简单的实现功能为目标了,它们反而会更加注重用户体验.我们常说的换肤(主题)功能--针对用户的喜好来提供一个可选的主题也是提高用户体验的方式之一.换肤功能不仅 ...

最新文章

  1. Spring Cloud Alibaba基础教程:使用Nacos实现服务注册与发现
  2. 重排序、hb、ConcurrentHashMap弱一致性(jdk1.6)
  3. shell去除字符串前所有的0
  4. JAX-WS Web Service
  5. Linux怎么删除虚拟硬盘,2017.05.10 qemu-nbd 全自动挂载/卸载 虚拟硬盘中所有可用分区 的 脚本...
  6. mysql 查询auto_increment_MySQL查询数据表的Auto_Increment(自增id)
  7. PP视频如何播放本地视频文件
  8. 笔试面试常考数据结构-单链表常用操作编程实现
  9. 值对于int32太大或太小怎么解决_数控车床加工螺纹时常见故障及解决方法
  10. C语言枚举类型(Enum)
  11. Linux acpi off学习
  12. 2017第八届(C/C++)B组蓝桥国赛题
  13. 华为OJ-数独(C语言、递归)
  14. Python爬虫——selenium爬取当当畅销图书排行
  15. 数学建模——数据包络分析步骤及程序详解
  16. 阿里云Maven镜像
  17. 双网卡上网冲突解决_【优特普.安防百科】交换机组网最常见的8大故障及解决方式...
  18. 台式计算机用什么网卡,台式电脑无线网卡怎么用 台式机无线网卡使用教程 - WiFi共享大师...
  19. python数码时钟代码_micro:bit 编程模拟时钟表盘
  20. MATLAB矩阵基本运算的实现(一)

热门文章

  1. SQL Server Management Studio 黑色主题
  2. 强行删除mac中的文件
  3. php+安装+curl_linux php安装curl扩展的方法
  4. 社区热议淘宝开源的优化定制JVM版本:Tabao JVM
  5. 中达优控触摸屏编程视频教程_YKBuilder中文版(中达优控触摸屏编程系统)V5.0.300 正式版...
  6. 单一参数的交流电路总结
  7. CA解扰 数字电视加密技术(EMM ECM)
  8. 手机QQ浏览器访问liferay工程页面异常解决
  9. 浅析海思麒麟970、960和950
  10. DNS服务器配置和测试