插件式换肤示例代码演示

1. 思路?


对于实现插件式换肤一般来说我们有2种比较常见的方法:

思路一:

android support library23.2 支持白天和黑夜切换的(最简单,直接切换主题即可),但是这个只有白天和黑夜2套皮肤的,并且这2套皮肤是在apk里边的;

思路二:

如果需要多套皮肤来回切换,可以把皮肤从服务器下载到本地,然后直接从本地获取皮肤,这里有个问题就是,如何获取另一个apk中的资源,这里指的就是apk中的ImageView图片;

2. 效果实现


2.1>:我们写一个SkinPaign的demo,然后就只在drawable下边放一张 image_src图片,然后运行,等运行成功后,会在这个目录下边生成一个 app-debug.apk;
图片.png
2.2>:然后把这个apk复制到桌面,给它重命名为 red.skin,或者其他的名称都可以,只要是 以.skin结尾的都可以,然后把这个 red.skin文件复制到 手机目录存储中,就表示我们已经把皮肤从服务器中下载到本地,这里只是做演示,就把它复制到手机存储中;
2.3>:然后我们可以通过获取另一个apk中的资源,从而获取到该资源中的图片,然后我点击一下换肤的按钮,就要把我手机上边的图片换成从手机目录中获取到的那张图片,达到 换肤的目的;

3. 效果如下图所示


点击之前:
图片.png
点击之后,就替换成从手机目录中 的 red.skin文件中获取的图片:
图片.png

4. 思路如下


1>:写一个demo,里边就只在drawable中放一张 图片,不做任何操作,然后会在app - build - outputs - apk下边生成一个 app-debug.apk;
2>:把这个apk复制到桌面,然后重命名为 red.skin,或者其他的以 .skin结尾的都可以;
3>:这里为了测试效果,就直接把 red.skin 文件复制到手机存储目录,表示已经从服务器下载了 皮肤到本地;
4>:然后点击 换肤的 按钮,就需要从手机目录中读取 red.skin文件,然后从中获取到 它drawable下边的那一张图片;
5>:然后用获取到的图片 替换 换肤按钮 下边的 这张图片即可;

5. 注意


这里会涉及到 ImageView的src、TextView的textColor中 资源是如何加载的源码知识,如果不是特别了解的,可以先去看下我之前的文章
ImageView的src、TextView的textColor等资源加载源码

1>:不能直接 new AssetManager()来创建对象,源码如下:
/**
     * Create a new AssetManager containing only the basic system assets.
     * Applications will not generally use this method, instead retrieving the
     * appropriate asset manager with {@link Resources#getAssets}.    Not for
     * use by applications.
     * {@hide}
     */
    public AssetManager() {
        synchronized (this) {
            if (DEBUG_REFS) {
                mNumRefs = 0;
                incRefsLocked(this.hashCode());
            }
            init(false);
            if (localLOGV) Log.v(TAG, "New asset manager: " + this);
            ensureSystemAssets();
        }
    }

发现 AssetManager上边是 hide,hide就表示只能够系统调用,我们如果想要调用只能用反射,来创建AssetManager对象;

2>:不能直接调用 asset.addAssetPath(String path),源码如下:
/**
     * Add an additional set of assets to the asset manager.  This can be
     * either a directory or ZIP file.  Not for use by applications.  Returns
     * the cookie of the added asset, or 0 on failure.
     * {@hide}
     */
    public final int addAssetPath(String path) {
         synchronized (this) {
            int res = addAssetPathNative(path, appAsLib);
            makeStringBlocks(mStringBlocks);
            return res;
        }
    }

发现这个方法也是 hide,hide就表示只能够系统调用,我们如果想要调用,就只能够通过反射,来调用 addAssetPath()方法

6. 代码如下


1>:activity_main布局文件如下:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context="cn.novate.essayjoke_day01.MainActivity">


    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/test_btn"
        android:text="换肤"
        />

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/test_iv"
        android:src="@drawable/image_src"
        android:layout_marginTop="10dp"
        />

</LinearLayout>

2>:在MainActivity中,点击 换肤 按钮,然后利用反射从手机目录中获取 red.skin中的资源图片,然后替换本地图片即可,代码如下:
/**
 * Email: 2185134304@qq.com
 * Created by JackChen 2018/4/14 20:03
 * Version 1.0
 * Params:
 * Description:  换肤测试 示例demo
*/
public class MainActivity extends BaseSkinActivity implements View.OnClickListener{

    private Button test_btn;
    private ImageView test_iv;

    @Override
    protected void initData() {

    }

    @Override
    protected void initView() {
        test_btn = (Button) findViewById(R.id.test_btn);
        test_btn.setOnClickListener(this);

        test_iv = (ImageView) findViewById(R.id.test_iv);
    }

    @Override
    protected void initTitle() {

    }

    @Override
    protected void setContentView() {
        setContentView(R.layout.activity_main);
    }

    /**
     * 1>:写一个demo,里边就只在drawable中放一张 图片,不做任何操作,然后会在app - build - outputs - apk下边生成一个 app-debug.apk;
     * 2>:把这个apk复制到桌面,然后重命名为 red.skin,或者其他的以 .skin结尾的都可以;
     * 3>:这里为了测试效果,就直接把 red.skin 文件复制到手机存储目录,表示已经从服务器下载了 皮肤到本地;
     * 4>:然后点击 换肤的 按钮,就需要从手机目录中读取 red.skin文件,然后从中获取到 它drawable下边的那一张图片;
     * 5>:然后用获取到的图片 替换 换肤按钮 下边的 这张图片;
     *
     *      以上,就达到换肤的目的;
     */
    @Override
    public void onClick(View v) {
        switch (v.getId()){

            case R.id.test_iv:
                try {
                    // 读取本地的一个 .skin里面的资源
                    Resources superRes = getResources() ;

                    // 创建AssetsManager
                    // 不能直接 new AssetManager() ;
                    // 通过反射来创建 asset对象
                    AssetManager asset = AssetManager.class.newInstance() ;
                    // 添加本地下载好的 资源皮肤,就是复制到 手机目录中的 red.skin
                    // 不能直接调用 addAssetPath()方法,只能通过反射调用 该方法
                    // 参数1:表示方法名称  参数2:表示方法里边的参数类型 如果是String path -> String.class int path -> int.class 等等

                    Method method = AssetManager.class.getDeclaredMethod("addAssetPath" , String.class) ;
                    method.setAccessible(true);  // 设置权限,防止addAssetPath()方法是私有private的

                    // 反射执行addAssetPath()方法   File.separator就和 "/" 是一样的
                    method.invoke(asset , Environment.getExternalStorageDirectory().getAbsolutePath() +
                            File.separator + "red.skin") ;
                    Resources resources = new Resources(asset , superRes.getDisplayMetrics() , superRes.getConfiguration()) ;

                    // 获取资源 id
                    // 参数1:表示图片的名称  参数2:表示类型  参数3:表示包名
                    int drawableId = resources.getIdentifier("image_src", "drawable", "cn.novate.skinpagin");
                    Drawable drawable = resources.getDrawable(drawableId) ;
                    test_iv.setImageDrawable(drawable);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                 break;

        }
    }
}

代码已上传至github:
https://github.com/shuai999/EssayJoke_day1-Two-Qi-.git

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

推荐阅读更多精彩内容