【前言说明】
此技术在android QQ2.0之后直到现在的QQ5.9版本也一直在使用,下来我会详细讲解代码及实现原理,此技术目前百度或者google上等一些博客都很少提及,大致原理是通过java反射Resources类,在Resources类中存在sPreloadedDrawables变量,这个便是关键点,它的作用就是实现Drawable图片的预加载,在APP运行时控件或通过Resources.getDrawable(resId)方法获取图片,然而getDrawable会从sPreloadedDrawables获取到图片,但是此方法也并不是万全之策,它可能会因为API的更改而更改。
1.效果展示
我们看到在此界面有三个按钮,接下来点击“立即换肤”按钮
此刻我们发现界面小机器人图标已被替换为蓝色背景的音乐图标
接下来我们点击“跳转”到AbcActivity界面看看效果。
我们发现在AbcActivity中图片也同样替换了,那么我们再看看AbcActivity的界面布局
在布局文件中,我们发现ImageView根本没有设置控件id,那么这个控件上的图片是怎么被替换的呢?接下来我们一起一步一步的分析。
二,原理分析及实现
在SkinEngine类中我们创建以下两个方法,并在initIntercepter方法中拦截Resources类中的sPreloadedDrawables变量。之后通过定义DrawablePreloadIntercepter做相关的拦截处理。
让DrawablePreloadIntercepter继承 LongSparseArray<Drawable.ConstantState>并重写get方法。
在get方法中我们首先建立mKeyToResourcesId变量,用于存储需要替换的皮肤资源,并添加对外的add方法,替换皮肤时,在Activity中将资源id添加到mKeyToResourcesId中。此时系统通过get方法获取资源时,判断资源文件对应的key在mKeyToResourcesId是否存在,如果不存在返回原本的图片资源,如果存在则调用SkinEngine类中的loadConstantState方法,加载皮肤资源。
在SkinEngine类的loadConstantState方法中,我们首先通过Resources的getValue方法拿到资源文件名,然后加载assets目录下的对应资源文件名的文件(此处可以改为从sd卡中加载)然后通过BitmapFactory创建Bitmap,并通过SkinnableBitmapDrawable类将Bitmap转换为Drawable.ConstantState对象。SkinnableBitmapDrawable类我已经写好了,可以拿去直接用。
此时就可以在Activity中使用换肤功能。使用方法如下
注意reloadDrawable方法,此方法只是为了刷新当前的Activity,在其他Activty不需要做此操作。
接来下说说SkinEngine.getInstances().run()方法,很简单,我们还需要反射Resources类中的mDrawableCache对象,它用于将sPreloadedDrawables的资源缓存下来,刚刚我们只是替换拦截了sPreloadedDrawables而已,此时我们需要将mDrawableCache中已经缓存的资源清空掉,然后通过reloadDrawable方法将页面中的图片重新从Resource中拿一下。此时你会发现换肤功能已经完成了。
接下来说说如何取消换肤,还记DrawablePreloadIntercepter中的mOldPreloadCache变量吗?当时在SkinEngine中的initIntercepter方法中我就已经将Resources类的sPreloadedDrawables变量保存下来了
使用方法,直接在Activity调用即可,然后在重新从Resources中拿一下资源。
好了,最后android的SkinEngine就已经完成了,接下来大家最关心的当然还是源码,那么我就无私的献上了,注意,下载需要1点积分,评论一下就赚回来了,就当时给我谢了这么久代码的一点点支持吧O(∩_∩)O~~
三,代码分享
http://download.csdn.net/detail/cc_want/9187187