Android应用程序换肤实现系列(三)

转载请标明出处: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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值