转载请标明出处:http://blog.csdn.net/EdisonChang/article/details/50044749
本节主要介绍android 主流换肤的第二种方式,皮肤做成普通压缩包格式,皮肤包括图标、字体、布局、交互风格等,然后由程序解析皮肤包文件,并动态替换本地资源。如果对通过android theme主题动态切换主题的方式有兴趣,可以阅读上一篇文章。
通过前一篇博文的介绍,我们知道如果应用需要能动态的扩展皮肤,而且素材以图片资源为主,那么就必须考虑动态下载皮肤。博文接下去介绍的几种方式都具备动态下载皮肤的功能,实际选择时如何取舍呢?
本节介绍的换肤方式,皮肤可以压缩成一个zip包(后面的扩展名可以任意),换肤前由应用提供下载入口,解压后更新替换皮肤。由于皮肤包是一个简单的通用的压缩包格式,所以这种方式非常适合用户在符合换肤素材的规则下(尺寸,命名等)个性化定制皮肤,自由度相当高。但它有一些显著的缺点,比如需要程序上自己处理加载、并解析各种资源文件等逻辑,整个过程效率比较低。再者如果资源文件较大,解析时更容易诱发内存溢出的错误,所以采用这类换肤方式时需要仔细评估,衡量利弊。
接下来,我们来了解这种换肤方式的实现步骤吧:
(1)对需要换肤的View需要的资源制定规则。这些规则包括资源的尺寸和命名方式,因为我们在解析过程中主要是以名称来寻找素材。本例demo以ImageView的背景为例,代码中为图片背景资源定义常量,皮肤包里面的对应的资源必须和代码 保持一致,
private static final String BG_NAME = "bg1.jpg";
(2)解析皮肤资源,动态设置View。这一点可以参考源码中SkinUtils.java文件中的getDrawable 方法,根据资源的类型,用android BitmapFactory 提供的api ,decodeFile 方法解析图片资源,然后根据资源名称key 保存在hashmap 中,提高效率 :
public static Drawable getDrawable(Context context, String res, int width, int height) {
if (TextUtils.isEmpty(res))
return null;
Drawable drawable = mapDynamicDrawable.get(res);
String selectedSkinPath = getSkinZipPath();
if (drawable == null) {
if (!res.endsWith("xml")) {
String path = selectedSkinPath + res;
Bitmap bm = getBitmapFromFile(new File(path), width, height);
if (bm != null) {
drawable = new BitmapDrawable(context.getResources(), bm);
mapDynamicDrawable.put(res, drawable);
}
} else {
String res_schemas = res.split("\\.")[0];
String res_normal = res_schemas + "_normal.png";
String res_press = res_schemas + "_pressed.png";
String res_normal_path = selectedSkinPath + res_normal;
String res_press_path = selectedSkinPath + res_press;
Bitmap bm_normal = getBitmapFromFile(new File(res_normal_path), width, height);
Bitmap bm_press = getBitmapFromFile(new File(res_press_path), width, height);
if (bm_normal != null && bm_press != null) {
Drawable dw_normal = new BitmapDrawable(context.getResources(), bm_normal);
Drawable dw_press = new BitmapDrawable(context.getResources(), bm_press);
drawable = new StateListDrawable();
((StateListDrawable) drawable).addState(new int[]{android.R.attr.state_focused, android.R.attr.state_pressed}, dw_press);
((StateListDrawable) drawable).addState(new int[]{}, dw_normal);
mapDynamicDrawable.put(res, drawable);
}
}
}
return drawable;
}
处理一个xml的资源类型,还必须同时对资源的_normal 态 和 _pressed 态进行解析,然后构造StateListDrawable 。构造drawable 后,在代码中,我们就可以简单调用setImageDrawable或setBackgroundDrawable 方法设置资源。
private void changeSkin() {
Drawable drawable = SkinUtils.getDrawable(getApplicationContext(), BG_NAME, 0, 0);
if (drawable != null) {
imageView.setImageDrawable(drawable);
textView.setText(R.string.skin_info2);
}
}
用这种方式换肤过程概括的说就是我们自己解析资源然后动态设置,如果更替的资源较多,就会导致代码量倍数增长,同时hashmap 缓存的drawable 也会大大增加 。还有一点需要注意的是,BitmapFactory 解析时需要根据View的大小设置 Options 的inSampleSize,这样可以有效的减少图片解析后占用的内存空间大小。
到这里,第二种换肤方式也介绍完了。博文介绍的是一些主要的实现步骤,需要了解更多细节,可以参考源码的demo程序。程序略去了下载解压皮肤包的操作,测试时请将bg1.jpg图片放到/sdcard/skin_zip/目录下,以保证解析时路径无误。
有任何疑问可以回复,欢迎继续关注换肤系列的下一篇
源码下载请点击此,欢迎下载,star