Android换肤之前人栽树后人撞树

最近没什么难点技术好写,有难点的技术没研究透也不敢写,我估摸着太久不更新也不太好,得显得我还存在着对技术的热爱。
这不正好上头派了个换肤的功能,那换肤不外乎是那些思路,用框架也行,自己写也不难,主要不想用别人写的,不然出BUG就不好整了。

1. 最基本的思路

那要换肤,不外乎就是if-else,判断某个参数,根据该参数来if-else,但这样写太Low了嘛,每次要加需要换肤的地方,都这样写,那就很难看。
我们可以封装一个类,需要换肤的时候传入原ResId,返回特定皮肤的ResId。
我的思路就是做一个规范,相应渠道的皮肤图片的名称就是原图名_渠道名,这样以后扩展不同的渠道只需要导入相应的资源就行,不用改代码。
比如说原图的名是 home_top_logo.png , 那渠道1的图片名就要用 home_top_logo_channelone.png, 那渠道2的图片名就要用 home_top_logo_channeltwo.png,大概是这么一个意思。

    String channelName = 渠道名

    public int getChannelRid(Context context, int res){
        String resName = context.getResources().getResourceEntryName(res);
        String suffix = "";
        if (channelName != null){
            suffix = "_"+ channelName.toLowerCase();
        }else { return res; }
        Resources resources = context.getResources();
        return resources.getIdentifier(resName+suffix, "drawable", context.getPackageName());
    }

这样就能传入ResId,返回对应渠道的ResId

2. 代码要优美

在这个思路的基础上,如果我们做离线换肤,那就需要提前把渠道图片放到包中。如果渠道多的话,就会导致你的drawable文件夹里:
home_top_logo.png
home_top_logo_channelone.png
home_top_logo_channeltwo.png
......
home_top_logo_channelsix.png
当资源多的情况下,我们就会看得很累,这时候我们正常的思路是什么,没错,就是分文件夹
但是res资源里面怎么分文件夹呢,这就需要用到Gradle来实现,如果你还不熟悉Gradle,那一定要去学。我们的思路就是开发时这些资源分文件夹,这样就很方便增删改查,打包时再一起打到包里就行。
这时候就要用到sourceSets了,简单的写

    sourceSets{
        main{
            res.srcDirs = ['src/main/res', 'src/main/reschone', 'src/main/reschtwo']
        }
    }

3. 插件包实现

按照之前的做法也只能实现离线换肤,如果要实现在线,还得需要用到插件化。插件化是什么,之前也写过简单的文章介绍。(写文章的好处就是忘记了能马上回忆起来)
https://www.jianshu.com/p/d813e3df1cb9
如果我们的这个插件包只是用来放资源,那就挺简单,无需去考虑平常插件化场景中的生命周期这类问题,只需要防止资源冲突就行。

我们把资源放到另外一个项目中,然后打成apk文件放到设备里,再在当前应用去引用apk的资源,这个就是主要的原理。

        String resName = context.getResources().getResourceEntryName(res);
        String suffix = "";
        if (channelName != null){
            suffix = "_"+ channelName.toLowerCase();
        }

        int rid = -1;
        try {
            if (pluginResources == null) {
                AssetManager assetManager = AssetManager.class.newInstance();
                AssetManager.class.getDeclaredMethod("addAssetPath", String.class)
                        .invoke(assetManager, path + suffix + ".apk");
                pluginResources = new Resources(assetManager, context.getResources().getDisplayMetrics(),
                        context.getResources().getConfiguration());
            }

            rid = pluginResources.getIdentifier(resName+suffix,
                    "drawable", 插件包名);
        }catch (Exception e){
            e.printStackTrace();
        }

正常我们这样就能获得到插件中资源的ResId了。but我们要获取资源必须要用这个pluginResources,不能用宿主context的Resources,因为不是同个表中的,会找不到。

最后说说解决资源冲突的方法。(这里不扩展去讲,因为这种技巧懂的都懂,不懂的我就说个思路,要自己去操作才能学会)
(1)可以去网上抄代码改AAPT (小心有些人的代码是不生效的)
(2)可以反编译去改再回编译,反正是你自己的包。
但是一般不会冲突

4. 前人栽树后人撞树

那么问题就来了,我这代码是接以前的人写的,他那封装好那个方法就是返回ResId,然后在几层之后的最外层再调用setXXXResource()

如果我们直接返回插件的ResId肯定是莫得了,找不到文件滴。这时候我们就需要一个标识来判断这个ID是宿主的ID还是插件的ID。
正常我们资源文件的ID格式都是固定的 PPTTNNNN格式,但是前面两个一般我们不改都是7f开头。
如果我们做了防冲突的操作,一般会改PP,这样就能根据这两个十六进制位来区分是哪个包里的资源。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,458评论 4 363
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,454评论 1 294
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,171评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,062评论 0 207
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,440评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,661评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,906评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,609评论 0 200
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,379评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,600评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,085评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,409评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,072评论 3 237
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,088评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,860评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,704评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,608评论 2 270

推荐阅读更多精彩内容

  • 用两张图告诉你,为什么你的 App 会卡顿? - Android - 掘金 Cover 有什么料? 从这篇文章中你...
    hw1212阅读 12,480评论 2 59
  • 前置知识 需要了解setContentView的具体流程 需要了解LayoutInflater的inflate过程...
    _柚子啊阅读 636评论 0 0
  • 前言: 换肤,目前包括静态换肤和动态换肤静态换肤 这种换肤的方式,也就是我们所说的内置换肤,就是在APP内部放置多...
    若无初见阅读 728评论 0 4
  • 今天再给大家带来一篇干货。 Android的主题换肤 ,可插件化提供皮肤包,无需Activity的重启直接实现无缝...
    _SOLID阅读 98,979评论 147 1,119
  • 概述 本文主要分享类似于酷狗音乐动态换肤效果的实现。 动态换肤的思路: 收集换肤控件以及对应的换肤属性 加载插件皮...
    昊空_6f4f阅读 578评论 0 6