Android 换肤技术资料整理

Android换肤技术总结

背景

纵观现在各种Android app,其换肤需求可以归为

- 白天/黑夜主题切换(或者别的名字,通常2套),如同花顺/自选股/天天动听等,UI表现为一个switcher。
- 多种主题切换,通常为会员特权,如QQ/QQ空间。

对于第一种来说,目测应该是直接通过本地theme来做的,即所有图片/颜色的资源都在apk里面打包了。

而对于第二种,则相对复杂一些,由于作为一种线上服务,可能上架新皮肤,且那么多皮肤包放在apk里面实在太占体积了,所以皮肤资源会在选择后再进行下载,也就不能直接使用android的那套theme。

技术方案

内部资源加载方案和动态下载资源下载两种。

动态下载可以称为一种黑科技了,因为往往需要hack系统的一些方法,所以在部分机型和新的API上有时候可能有坑,但相对好处则很多

- 图片/色值等资源由于是后台下发的,可以随时更新
- APK体积减小
- 对应用开发者来说,换肤几乎是透明的,不需要关心有几套皮肤
- 可以作为增值服务卖钱!!

内部资源加载方案

内部资源加载都是通过android本身那套theme来做的,相对业务开发来说工作量更大(需要定义attr和theme),不同方案类似地都是在BaseActivity里面做setTheme,差别主要在解决以下2个问题的策略:

- setTheme后如何实时刷新,而不用重新创建页面(尤其是listview里面的item)。
- 哪些view需要刷新,刷新什么(背景?字体颜色?ImageView的src?)。

自定义view

MultipleTheme

做自定义view是为了在setTheme后会去立即刷新,更新页面UI对应资源(如TextView替换背景图和文字颜色),在上述项目中,则是通过对rootView进行遍历,对所有实现了ColorUiInterface的view/viewgroup进行setTheme操作来实现即使刷新的。

显然这样太重了,需要把应用内的各种view/viewgroup进行替换。

手动绑定view和要改变的资源类型

Colorful

这个…我们看看用法吧….

ViewGroupSetter listViewSetter = new ViewGroupSetter(mNewsListView); // 绑定ListView的Item View中的news_title视图,在换肤时修改它的text_color属性 listViewSetter.childViewTextColor(R.id.news_title, R.attr.text_color); // 构建Colorful对象来绑定View与属性的对象关系 mColorful = new Colorful.Builder(this) .backgroundDrawable(R.id.root_view, R.attr.root_view_bg) // 设置view的背景图片 .backgroundColor(R.id.change_btn, R.attr.btn_bg) // 设置背景色 .textColor(R.id.textview, R.attr.text_color) .setter(listViewSetter) // 手动设置setter .create(); // 设置文本颜色

我就是想换个皮肤,还得在activity里自己去设置要改变哪个view的什么属性,对应哪个attribute?是不是成本太高了?而且activity的逻辑也很容易被弄得乱七八糟。

动态资源加载方案

resource替换

覆盖application的getResource方法,实现自己的resource,优先加载本地皮肤包文件夹下的资源包,对于性能问题,可以通过attribute或者资源名称规范(如需要换肤则用skin_开头)来优化,从而不对不换肤的资源进行额外检查开销。

不过由于Android5.1源码里,drawable初始化的时候调用的是loadDrawable,而不是resource.getDrawable,而loadDrawable是私有的方法,无法覆盖,所以虽然很方便,却无法继续使用(不用关心任何皮肤相关的事情,android:color指定颜色就行了,神奇滴会自动换肤)。

自定义LayoutInflator.Factory

开源项目可参照Android-Skin-Loader。

即setFactory使用自定义的LayoutInflator.Factory,可以重点关注该项目中的SkinInflaterFactory和SkinManager(实现了自己的getColor、getDrawable、getBitmap、getColorStateList等等方法)。

需要自定义一个tag比如app:customStyle,重写所有的style,转成set方法,这样带来的牺牲就是增加了换肤的成本,要写很多style,自己去set,并不完全透明了。

Hack Resources internally

黑科技方法,直接对Resources进行hack,Resources.Java:

// Information about preloaded resources. Note that they are not // protected by a lock, because while preloading in zygote we are all // single-threaded, and after that these are immutable. private static final LongSparseArray<Drawable.ConstantState>[] sPreloadedDrawables; private static final LongSparseArray<Drawable.ConstantState> sPreloadedColorDrawables = new LongSparseArray<Drawable.ConstantState>(); private static final LongSparseArray<ColorStateList> sPreloadedColorStateLists = new LongSparseArray<ColorStateList>();

直接对Resources里面的这三个LongSparseArray进行替换,由于apk运行时的资源都是从这三个数组里面加载的,所以只要采用interceptor模式:

public class DrawablePreloadInterceptor extends LongSparseArray<Drawable.ConstantState>

自己实现一个LongSparseArray,并通过反射set回去,就能实现换肤,具体getDrawable等方法里是怎么取preload数组的,可以自己看Resources的源码。

等等,就这么简单?,NONO,少年你太天真了,怎么去加载xml,9patch的padding怎么更新,怎么打包/加载自定义的皮肤包,drawable的状态怎么刷新,等等。这些都是你需要考虑的,在存在插件的app中,还需要考虑是否会互相覆盖resource id的问题,进而需要修改apt,把resource id按位放在2个range。

手Q和独立版QQ空间使用的是这种方案,效果挺好。

总结

尽管动态加载方案比较黑科技,可能因为系统API的更改而出问题,但相对来说

好处有

- 灵活性高,后台可以随时更新皮肤包
- 相对透明,开发者几乎不用关心有几套皮肤,不用去定义各种theme和attr,甚至连皮肤包的打包都可以交给设计或者专门的同学
- apk体积节省

存在的问题

没有完善的开源项目,如果我们采用动态加载的第二种方案,需要的项目功能包括:

- 自定义皮肤包结构
- 换肤引擎,加载皮肤包资源并load,实时刷新。
- 皮肤包打包工具
- 对各种rom的兼容

如果有这么一个项目的话,就一劳永逸了,有兴趣的同学可以联系一下,大家一起搞一搞。

内部加载方案大同小异,主要解决的都是即时刷新的问题,然而从目前的一些开源项目来看,仍然没有特别简便的方案。让我选的话,我宁愿让界面重新创建,比如重启activity,或者remove所有view再添加回来(或者你可能想遍历rootview,然后一个个检查是否需要换肤然后set…)。

感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!

时间: 2024-09-23 03:55:42

Android 换肤技术资料整理的相关文章

动态CSS,换肤技术

css|动态 常见的例子就是:一个站点上有多个页面样式提供浏览者选择. 同时,在选择了某样式后,再次打开该页面时,将仍然保持该样式. 自然会想到了Cookie技术 下面是HTML代码部分(另外再加需要的CSS文件就可以使用了): <HTML> <HEAD> <link ID="skin" rel="stylesheet" type="text/css"> <TITLE>换肤技术</TITLE&

TheBeerHouse网站项目学习笔记(1)----换肤技术

对于ASP.NET学习的中期,TheBeerHouse 项目是一个不错的选择,这个项目几乎囊括了所有ASP.NET 2.0 下所有的技术点,而且其设计的类图架构知识值得我们借鉴.关于此项目的介绍,在此不罗嗦,可以参看如下 地址: 1. 源码下载: http://www.asp.net/Downloads/starter-kits/the-beer-house 2. 功能技术点介绍: http://www.codeplex.com/TheBeerHouse 3. 该项目真实网站: http://w

软件换肤技术在BCB中的实现

本文配套源码 看一个软件是否优秀,除了功能健全.性能稳定.容易操作之外,软件界面的美观越来越受到人们的关注.人们不满足于传统的矩形windows窗体,不规则窗体的软件到处可见,更进一步发展到软件界面由用户根据自己的爱好自由控制,真正实现了软件对用户的友好性.而要实现这一功能,软件换肤技术就是基础.下面我就介绍一种简单明了的方法来制作一个登陆对话框 ,本程序编程环境C++Builder: 首先由程序员根据功能要求定义界面元素(控件),特别值得注意的是控件的名字.如下图: 图一:元素界面 其次,美工

android换肤功能 如何动态获取控件中背景图片的资源id?_Android

这个是在在做一个换肤功能时遇到的问题.   对于换肤,网上都有示例,可以从别的皮肤安装包中读取所要的资源,前提是你必须先持有这个资源的引用名称,像R.drawable.background(喂,这不是废话嘛).这个换肤的方案原理就是,自身应用的资源名称是R.drawable.background,那皮肤包中应该也是这个名称,然后通过这个名称获取该资源在皮肤包中的具体id,代码: //先获取本地资源引用名称,type name是R.drawable.background中的"drawable&qu

网页常见的换肤技术

常见的例子就是:一个站点上有多个页面样式提供浏览者选择.  同时,在选择了某样式后,再次打开该页面时,将仍然保持该样式.  自然会想到了Cookie技术  下面是HTML代码部分(另外再加需要的CSS文件就可以使用了):  //        <HTML>  //         //        <HEAD>  //         //        <link ID="skin" rel="stylesheet" type=&q

动态CSS,换肤技术_ASP基础

见的例子就是:一个站点上有多个页面样式提供浏览者选择. 同时,在选择了某样式后,再次打开该页面时,将仍然保持该样式. 自然会想到了Cookie技术  以下是程序代码: <HTML> <HEAD> <link ID="skin" rel="stylesheet" type="text/css"> <TITLE>换肤技术</TITLE> <SCRIPT LANGUAGE=javascr

android换肤功能 如何动态获取控件中背景图片的资源id?

这个是在在做一个换肤功能时遇到的问题. 对于换肤,网上都有示例,可以从别的皮肤安装包中读取所要的资源,前提是你必须先持有这个资源的引用名称,像R.drawable.background(喂,这不是废话嘛).这个换肤的方案原理就是,自身应用的资源名称是R.drawable.background,那皮肤包中应该也是这个名称,然后通过这个名称获取该资源在皮肤包中的具体id,代码: //先获取本地资源引用名称,type name是R.drawable.background中的"drawable"

javascript实现动态CSS换肤技术的脚本_javascript技巧

<SCRIPT LANGUAGE=javascript>     <!--     function SetCookie(name,value){         var argv=SetCookie.arguments;         var argc=SetCookie.arguments.length;         var expires=(2<argc)?argv[2]:null;         var path=(3<argc)?argv[3]:null; 

NoSQL Databases技术资料整理汇总

0 Reference NoSQL论文 在 Stuttgart Media 大学的 Christof Strauch 历时8个月(2010年6月-2011年2月)完成了一篇150页长的NoSQL相关的论文, 对NoSQL的各个方面做了探讨 http://www.christof-strauch.de/nosqldbs.pdf 分布式系统领域经典论文翻译集 http://duanple.blog.163.com/blog/static/709717672011330101333271/ 2010