关闭 x
IT技术网
    技 采 号
    ITJS.cn - 技术改变世界
    • 实用工具
    • 菜鸟教程
    IT采购网 中国存储网 科技号 CIO智库

    IT技术网

    IT采购网
    • 首页
    • 行业资讯
    • 系统运维
      • 操作系统
        • Windows
        • Linux
        • Mac OS
      • 数据库
        • MySQL
        • Oracle
        • SQL Server
      • 网站建设
    • 人工智能
    • 半导体芯片
    • 笔记本电脑
    • 智能手机
    • 智能汽车
    • 编程语言
    IT技术网 - ITJS.CN
    首页 » 安卓开发 »Android 更换皮肤思路及解决方案

    Android 更换皮肤思路及解决方案

    2015-06-04 00:00:00 出处:工匠若水
    分享

    该文博客要给大家分享的一个关于Android应用换肤的Demo,大家可以到我的github去下载demo,以后博文涉及到的代码均会上传到github中统一管理。

    github地址:https://github.com/devilWwj/Android-skin-update

    思路

    换肤功能一般有什么?
    元素一般有背景颜色、字体颜色、图片、布局等等

    我们知道Android中有主题Theme还有style,theme是针对整个activity的,而style可以针对指定控件,假如比较少的替换可以在app内做,但假如需要动态来做,可以选择下面这种思路:

    把app和skin分开,将skin做成一个apk,作为一个插件来提供给app使用,这样可以做到在线下载皮肤,然后动态更换皮肤

    下面这个demo,小巫是建立了一个res的工程项目,简单提供了一个colors.xml,在里面指定了背景颜色和按钮颜色:

    < xml version="1.0" encoding="utf-8" >
    <resources>
        <color name="day_btn_color">#E61ABD</color>
        <color name="day_background">#38F709</color>
    
        <color name="night_btn_color">#000000</color>
        <color name="night_background">#FFFFFF</color>
    </resources>

    里面没有任何逻辑代码,只提供资源文件,然后我们导出为skin.apk文件,复制到目标项目的assets中去。

    因为这里不涉及到下载皮肤这个操作,所以直接放到assets目录下,然后在程序中把assets下的apk文件复制到sd卡中.
    在程序中提供一个皮肤包管理器

    package com.devilwwj.skin;
    
    import java.io.File;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Method;
    
    import android.content.Context;
    import android.content.pm.PackageInfo;
    import android.content.pm.PackageManager;
    import android.content.res.AssetManager;
    import android.content.res.Resources;
    import android.os.AsyncTask;
    
    /**
     * 皮肤包管理器
     * 
     * @author devilwwj
     * 
     */
    public class SkinPackageManager {
        private static SkinPackageManager mInstance;
        private Context mContext;
    
        /**
         * 当前资源包名
         */
        public String mPackageName;
    
        /**
         * 皮肤资源
         */
        public Resources mResources;
    
        public SkinPackageManager(Context mContext) {
            super();
            this.mContext = mContext;
        }
    
        /**
         * 获取单例
         * 
         * @param mContext
         * @return
         */
        public static SkinPackageManager getInstance(Context mContext) {
            if (mInstance == null) {
                mInstance = new SkinPackageManager(mContext);
            }
            return mInstance;
        }
    
        /**
         * 从assets中复制apk到sd中
         * 
         * @param context
         * @param filename
         * @param path
         * @return
         */
        public boolean copyApkFromAssets(Context context, String filename,
                String path) {
            boolean copyIsFinish = false;
    
            try {
                // 打开assets的输入流
                InputStream is = context.getAssets().open(filename);
                File file = new File(path);
                // 创建一个新的文件
                file.createNewFile();
                FileOutputStream fos = new FileOutputStream(file);
                byte[] temp = new byte[1024];
                int i = 0;
                while ((i = is.read(temp)) > 0) {
                    fos.write(temp, 0, i); // 写入到文件
                }
    
                fos.close();
                is.close();
                copyIsFinish = true;
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return copyIsFinish;
    
        }
    
        /**
         * 异步加载皮肤资源
         * 
         * @param dexPath
         *            需要加载的皮肤资源
         * @param callback
         *            回调接口
         */
        public void loadSkinAsync(String dexPath, final loadSkinCallBack callback) {
            new AsyncTask<String, Void, Resources>() {
    
                @Override
                protected void onPreExecute() {
                    super.onPreExecute();
                    if (callback != null) {
                        callback.startloadSkin();
                    }
                }
    
                @Override
                protected Resources doInBackground(String... params) {
                    try {
                        if (params.length == 1) {
                            //
                            String dexPath_tmp = params[0];
                            // 得到包管理器
                            PackageManager mpm = mContext.getPackageManager();
                            // 得到包信息
                            PackageInfo mInfo = mpm.getPackageArchiveInfo(
                                    dexPath_tmp, PackageManager.GET_ACTIVITIES);
                            mPackageName = mInfo.packageName;
    
                            // AssetManager实例
                            AssetManager assetManager = AssetManager.class
                                    .newInstance();
                            // 通过反射调用addAssetPath方法
                            Method addAssetPath = assetManager.getClass()
                                    .getMethod("addAssetPath", String.class);
                            addAssetPath.invoke(assetManager, dexPath_tmp);
    
                            // 得到资源实例
                            Resources superRes = mContext.getResources();
                            // 实例化皮肤资源
                            Resources skinResource = new Resources(assetManager,
                                    superRes.getDisplayMetrics(),
                                    superRes.getConfiguration());
                            // 保存资源路径
                            SkinConfig.getInstance(mContext).setSkinResourcePath(
                                    dexPath_tmp);
                            return skinResource;
                        }
                    } catch (Exception e) {
                        return null;
                    }
                    return null;
                }
    
                @Override
                protected void onPostExecute(Resources result) {
                    super.onPostExecute(result);
                    mResources = result;
    
                    // 这里执行回调方法
                    if (callback != null) {
                        if (mResources != null) {
                            callback.loadSkinSuccess();
                        } else {
                            callback.loadSkinFail();
                        }
                    }
                }
    
            }.execute(dexPath);
        }
    
        public static interface loadSkinCallBack {
            public void startloadSkin();
    
            public void loadSkinSuccess();
    
            public void loadSkinFail();
        }
    
    }

    重点关注这个类,里面提供了一个异步方法对包和asset进行操作,这里用到了反射机制,反射调用addAssetPath来添加assets的路径,这个路径就是我们skin.apk的路径。具体细节,各位查看代码。

    我们在Activity界面中使用上面提供的方法:

    package com.devilwwj.skin;
    
    import android.app.Activity;
    import android.content.res.Resources;
    import android.os.Bundle;
    import android.os.Environment;
    import android.util.Log;
    import android.view.View;
    import android.view.View.OnClickListener;
    import android.widget.Button;
    import android.widget.TextView;
    
    import com.devilwwj.skin.SkinPackageManager.loadSkinCallBack;
    /**
     * 功能:切换皮肤
     * @author devilwwj
     *
     */
    public class MainActivity extends Activity implements OnClickListener,
            ISkinUpdate {
        private static final String APK_NAME = "skin.apk";
        private static final String DEX_PATH = Environment
                .getExternalStorageDirectory().getAbsolutePath() + "/skin.apk";
        private Button dayButton;
        private Button nightButton;
        private TextView textView;
        private boolean nightModel = false;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            dayButton = (Button) findViewById(R.id.btn_day);
            nightButton = (Button) findViewById(R.id.btn_night);
            textView = (TextView) findViewById(R.id.text);
    
            // 把apk文件复制到sd卡
            SkinPackageManager.getInstance(this).copyApkFromAssets(this, APK_NAME,
                    DEX_PATH);
    
        }
    
        @Override
        protected void onResume() {
            super.onResume();
            if (SkinPackageManager.getInstance(this).mResources != null) {
                updateTheme();
            }
        }
    
        @Override
        public void onClick(View v) {
            switch (v.getId()) {
            case R.id.btn_day:
                nightModel = false;
                loadSkin();
                break;
            case R.id.btn_night:
                nightModel = true;
                loadSkin();
                break;
    
            default:
                break;
            }
        }
    
        /**
         * 加载皮肤
         */
        private void loadSkin() {
            SkinPackageManager.getInstance(this).loadSkinAsync(DEX_PATH,
                    new loadSkinCallBack() {
    
                        @Override
                        public void startloadSkin() {
                            Log.d("xiaowu", "startloadSkin");
                        }
    
                        @Override
                        public void loadSkinSuccess() {
                            Log.d("xiaowu", "loadSkinSuccess");
                            // 然后这里更新主题
                            updateTheme();
                        }
    
                        @Override
                        public void loadSkinFail() {
                            Log.d("xiaowu", "loadSkinFail");
                        }
                    });
        }
    
        @Override
        public void updateTheme() {
            Resources mResource = SkinPackageManager.getInstance(this).mResources;
            if (nightModel) {
                // 假如是黑夜的模式,则加载黑夜的主题
                int id1 = mResource.getIdentifier("night_btn_color", "color",
                        "com.devilwwj.res");
                nightButton.setBackgroundColor(mResource.getColor(id1));
                int id2 = mResource.getIdentifier("night_background", "color",
                        "com.devilwwj.res");
                nightButton.setTextColor(mResource.getColor(id2));
                textView.setTextColor(mResource.getColor(id2));
    
            } else {
                // 假如是白天模式,则加载白天的主题
                int id1 = mResource.getIdentifier("day_btn_color", "color",
                        "com.devilwwj.res");
                dayButton.setBackgroundColor(mResource.getColor(id1));
                int id2 = mResource.getIdentifier("day_background", "color",
                        "com.devilwwj.res");
                dayButton.setTextColor(mResource.getColor(id2));
                textView.setTextColor(mResource.getColor(id2));
            }
    
        }
    
    }

    我们可以保存一个模式,比如黑夜白天模式,每次启动按照前面保存的模式来显示皮肤。我们可以看到上面是通过调用getIdentifier方法来得到指定的资源的id,name是我们在资源文件中指定的名字。

    最后,各位自己跑一遍这样的流程:

    1. 导出res的apk文件
    2. 复制到目标项目的assets目录下
    3. 查看切换皮肤的效果

    参考博文:http://blog.csdn.net/yuanzeyao/article/details/42390431

    上一篇返回首页 下一篇

    声明: 此文观点不代表本站立场;转载务必保留本文链接;版权疑问请联系我们。

    别人在看

    正版 Windows 11产品密钥怎么查找/查看?

    还有3个月,微软将停止 Windows 10 的更新

    Windows 10 终止支持后,企业为何要立即升级?

    Windows 10 将于 2025年10 月终止技术支持,建议迁移到 Windows 11

    Windows 12 发布推迟,微软正全力筹备Windows 11 25H2更新

    Linux 退出 mail的命令是什么

    Linux 提醒 No space left on device,但我的空间看起来还有不少空余呢

    hiberfil.sys文件可以删除吗?了解该文件并手把手教你删除C盘的hiberfil.sys文件

    Window 10和 Windows 11哪个好?答案是:看你自己的需求

    盗版软件成公司里的“隐形炸弹”?老板们的“法务噩梦” 有救了!

    IT头条

    液冷服务器概念股走强,博汇、润泽等液冷概念股票大涨

    01:17

    亚太地区的 AI 驱动型医疗保健:2025 年及以后的下一步是什么?

    16:30

    智能手机市场风云:iPhone领跑销量榜,华为缺席引争议

    15:43

    大数据算法和“老师傅”经验叠加 智慧化收储粮食尽显“科技范”

    15:17

    严重缩水!NVIDIA将推中国特供RTX 5090 DD:只剩24GB显存

    00:17

    技术热点

    在windows 7桌面右键菜单上添加直接卸载USB设备的快捷菜单选项

    12个Java长久占居主要地位的原因

    SQL Server创建数据库的命令

    SQL Server 带列名导出到excel的实际操作

    MySQL字符串连接函数repeat()

    使用SQL Server日志转移实现高可用性

      友情链接:
    • IT采购网
    • 科技号
    • 中国存储网
    • 存储网
    • 半导体联盟
    • 医疗软件网
    • 软件中国
    • ITbrand
    • 采购中国
    • CIO智库
    • 考研题库
    • 法务网
    • AI工具网
    • 电子芯片网
    • 安全库
    • 隐私保护
    • 版权申明
    • 联系我们
    IT技术网 版权所有 © 2020-2025,京ICP备14047533号-20,Power by OK设计网

    在上方输入关键词后,回车键 开始搜索。Esc键 取消该搜索窗口。