Android更换皮肤解决方案

转载请注明出处:IT_xiao小巫

本篇博客要给大家分享的一个关于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>() {@Overrideprotected void onPreExecute() {super.onPreExecute();if (callback != null) {callback.startloadSkin();}}@Overrideprotected 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;}@Overrideprotected 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;@Overrideprotected 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);}@Overrideprotected void onResume() {super.onResume();if (SkinPackageManager.getInstance(this).mResources != null) {updateTheme();}}@Overridepublic 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() {@Overridepublic void startloadSkin() {Log.d("xiaowu", "startloadSkin");}@Overridepublic void loadSkinSuccess() {Log.d("xiaowu", "loadSkinSuccess");// 然后这里更新主题updateTheme();}@Overridepublic void loadSkinFail() {Log.d("xiaowu", "loadSkinFail");}});}@Overridepublic 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

Android更换皮肤解决方案相关推荐

  1. 轻松实现Android 更换皮肤(主题)

    目前很多app都具有换肤功能,可以根据用户自己的喜好定制自己的界面,比如新浪微博,网易新闻等等.今天这里我就是要介绍一种机制实现app换肤. 我找了几款app换肤的应用,换肤基本都是更换了界面的Ico ...

  2. android 更换皮肤项目

    android换肤项目是本人学习android不到一个月做的第一个项目,所以有很多可以优化的地方,仅给大家提供一个参考. package wang.sc; import android.app.Act ...

  3. android+自定义皮肤,android studio自定义更换皮肤详细图文教程

    android studio这款app程序开发软件内也内置了多种皮肤主题,程序开发人员如果感觉一种皮肤太过单调乏味,可以选择使用软件内的其他皮肤风格,软件默认的皮肤是IntelliJ,还有黑色的Dra ...

  4. [转载]Android实现更换皮肤功能

    原文地址:Android实现更换皮肤功能作者:兜兜 转载请注明出处,商用请与本人联系. 本系列专题培训适用范围:初级Android程序员,即有J2SE基础和Android初级水平.J2SE基础是指掌握 ...

  5. apk分享: Android应用更换皮肤功能的实现思路教程。

    Android 的发展确实太快了,每年的都有很多新东西出现,想要覆盖所有新东西感觉也不太可能,我这里主要说一下主要的 Android 的主要新技术发展,其实了解 Android 的发展趋势,可能对开发 ...

  6. android 自定义皮肤,仿墨迹天气在Android App中实现自定义zip皮肤更换

    在这里谈一下墨迹天气的换肤实现方式,不过首先声明我只是通过反编译以及参考了一些网上其他资料的方式推测出的换肤原理, 在这里只供参考. 若大家有更好的方式, 欢迎交流. 墨迹天气下载的皮肤就是一个zip ...

  7. android应用换肤功能,如何给Android应用更换皮肤

    Android应用更换皮肤功能的实现思路 我们可以将皮肤包做成一个apk文件,在应用apk中读取皮肤包中的数据. 应用apk读取皮肤包apk的数据的条件 1. 有相同签名 2. AndroidMani ...

  8. C#如何使用skinEngine控件换肤(更换皮肤)?

    在使用C#开发窗体程序时,原始的皮肤方案比较难看,因此开发者需要更换皮肤,以提高客户体验.skinEngine控件就是一个很好的解决方案,它可以降低开发者开发新皮肤的成本,一个新手也可以很快的掌握更换 ...

  9. React+antd在线上动态更换皮肤主题

    开始 因为项目里一个更换皮肤的功能,虽然antd官网给出了更改主题的方法,但那是静态的,没办法在线上更换,然后发现网上在这方面的资料也并不多,这个业务难道不是应该很普遍吗?然后我就花了一些时间去解决了 ...

  10. android+模拟器皮肤,自定义android模拟器皮肤和键盘映射

    我想为Android模拟器创建自己的皮肤.我有照片购买了一些皮肤图像. 有一些额外的按钮,我需要映射,以便点击它时应该生成一个特定的事件. 在我的个性化皮肤文件夹的布局文件看起来是这样的:自定义and ...

最新文章

  1. 同济大学计算机学院徐老师,第十八届同济大学程序设计竞赛暨高校网络友谊赛圆满落幕...
  2. android:background大小,小Demo小知识-android:foreground与android:background
  3. 火山引擎进军云市场,计划未来三年服务十万客户
  4. python定时器 循环_python从写定时器学习Thread
  5. HDU 4422 The Little Girl who Picks Mushrooms(简单题)
  6. 数据 3 分钟 | 阿里云数据库 2020 技术年报发布、TiDB 开启 Hacking Camp、达梦云数据库免费体验...
  7. 《深入浅出MySQL:数据库开发、优化与管理维护(第2版)》一一1.2 MySQL的安装...
  8. 《Java虚拟机原理图解》1.1、class文件基本组织结构
  9. php值传递和引用传递
  10. 什么是编译、什么是连接。C++代码如何变成机器码,如何跨平台和嵌入式?嵌入式开发
  11. fei 正则表达式_正则表达式 匹配 中文/日文/韩文
  12. 防火墙结构之屏蔽主机体系结构
  13. 基于Django 文档1.11 自解+补完 学习django ---part1
  14. 解决CentOS小数字键盘失效,按NumLock无效的方法
  15. 杨绛:她和谁都不争,和谁争都不屑
  16. Mysql 之百万数据导入
  17. 如何通过日志恢复被删除的数据
  18. Eclipse中离线安装ADT插件详细教程及下载链接
  19. 如何用python画配电系统图
  20. java jacob下载_jacob下载包含jar包和src

热门文章

  1. Android Studio 设置背景色、修改前进后退快捷键
  2. 区块链火了 市场热度最么高
  3. 主流HTML5游戏开发引擎的分析和对比
  4. R语言t检验,秩和检验,fdr的案例分析
  5. 二值化神经网络(BNN)综述
  6. 3.Adaptive AUTOSAR 架构详解
  7. 数据结构实验——用链表实现简单的多项式乘法
  8. 类和对象8:数值方法
  9. SpringBoot优缺点分析
  10. latex beamer 空一行_Beamer——基于LaTex的Slides制作