关闭 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

    上一篇返回首页 下一篇

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

    别人在看

    Destoon 模板存放规则及语法参考

    Destoon系统常量与变量

    Destoon系统目录文件结构说明

    Destoon 系统安装指南

    Destoon会员公司主页模板风格添加方法

    Destoon 二次开发入门

    Microsoft 将于 2026 年 10 月终止对 Windows 11 SE 的支持

    Windows 11 存储感知如何设置?了解Windows 11 存储感知开启的好处

    Windows 11 24H2 更新灾难:系统升级了,SSD固态盘不见了...

    小米路由器买哪款?Miwifi热门路由器型号对比分析

    IT头条

    Synology 对 Office 套件进行重大 AI 更新,增强私有云的生产力和安全性

    01:43

    StorONE 的高效平台将 Storage Guardian 数据中心占用空间减少 80%

    11:03

    年赚千亿的印度能源巨头Nayara 云服务瘫痪,被微软卡了一下脖子

    12:54

    国产6nm GPU新突破!砺算科技官宣:自研TrueGPU架构7月26日发布

    01:57

    公安部:我国在售汽车搭载的“智驾”系统都不具备“自动驾驶”功能

    02:03

    技术热点

    如何删除自带的不常用应用为windows 7减负

    MySQL中多表删除方法

    改进的二值图像像素标记算法及程序实现

    windows 7 32位系统下手动修改磁盘属性例如M盘修改为F盘

    windows 7中怎么样在家庭组互传文件

    Linux应用集成MySQL数据库访问技巧

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

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