这里使用的是开源框架Android-skin-loader。这个框架已经停止维护了,能满足基本功能需求。

基础使用

添加依赖
将这个库的lib作为module导入,这样方便你根据自己的需求去添加一些功能。

使用

  • 继承BaseActivity或者BaseFragmentActivity或者BaseFragment
  • 在Application中初始化
public class YourApplication extends Application {public void onCreate() {super.onCreate();// Must call init first SkinManager.getInstance().init(this);SkinManager.getInstance().load();}
}
  • 在布局中标识需要换肤的view
//命名空间
xmlns:skin="http://schemas.android.com/android/skin"<TextViewskin:enable="true" />
  • 从已生成的皮肤文件中设置皮肤
File skin = new File("skin path");
SkinManager.getInstance().load(skin.getAbsolutePath(),new ILoaderListener() {@Overridepublic void onStart() {}@Overridepublic void onSuccess() {}@Overridepublic void onFailed() {}});

生成皮肤文件

生成皮肤文件apk

创建一个App module(记住不是library module)。这个module不需要java文件,可以直接将module_name/src/main/java目录删除。然后在res目录下添加你需要更换的资源文件。记住:需要更换的资源文件必须和主module中的资源文件名字保持一致。
然后直接打包生成apk文件。

复制到主module

将apk文件复制到主module的某个目录下,比如main_module/src/main/assets目录。

更改皮肤文件后缀名

为了防止皮肤文件被用户点击安装,可以将文件后缀改成.skin。或者你自定义一个后缀名。

生成多个皮肤文件

要生成多个皮肤文件,直接在gradle做配置,而无需创建多个module。在skin_module/src目录下创建不同种类的皮肤文件目录,与main同级。这样就可以编译生成不同皮肤的apk。

  • 添加buildType
 android {buildTypes{bmw {}benz {}toyota {}}}
  • 自定义task 自定义一个task,通过获取buildTypes来生成对应的文件夹。
task createAllBuildTypeChildDir() {//遍历main/res下的子目录,然后为不同的buildType生成对应的目录。这里要使用project.rootDir来表示根目录,这样可以自动适配不同的电脑系统def file = new File("${project.rootDir}/skin_module/src/main/res")file.listFiles().each { childFile ->def dirName =  childFile.nameproject.extensions.each { extension ->extension.getByName("android").properties.each { property ->if (property.key == "buildTypes") {property.value.each { value ->def variantName =  value["name"]if (("debug" != variantName) && ("release" != variantName)){def dest = new File("${project.rootDir}/skin_module/src/"+variantName+"/res",dirName)if (!dest.exists()){dest.mkdirs()}}}}}}}}

修改不同文件夹下的资源文件,再去编译对应的皮肤文件即可。

`标识需要换肤的view

在SkinConfig中定义了命名空间,在需要换肤的布局中添加该命名空间。

public class SkinConfig {public  static final String  NAMESPACE = "http://schemas.android.com/android/skin";
}

这个命名空间如何使用到的? 在SkinInflaterFactory的onCreateView方法中先判断了布局中是否存在这个命名空间

boolean isSkinEnable = attrs.getAttributeBooleanValue(SkinConfig.NAMESPACE, SkinConfig.ATTR_SKIN_ENABLE, false);

也就是只有在布局中使用了这个命名空间的布局才能被换肤。

SkinInflaterFactory

SkinInflaterFactory实现了Layoutinflater.Factory。这个类在setContentView(layout)方法之前调用,可以过滤并修改我们需要换肤的view。

  • onCreateView
@Overridepublic View onCreateView(String name, Context context, AttributeSet attrs) {// if this is NOT enable to be skined , simplly skip it boolean isSkinEnable = attrs.getAttributeBooleanValue(SkinConfig.NAMESPACE, SkinConfig.ATTR_SKIN_ENABLE, false);if (!isSkinEnable){return null;}View view = createView(context, name, attrs);if (view == null){return null;}parseSkinAttr(context, attrs, view);return view;}
  • createView
private View createView(Context context, String name, AttributeSet attrs) {View view = null;try {if (-1 == name.indexOf('.')){if ("View".equals(name)) {view = LayoutInflater.from(context).createView(name, "android.view.", attrs);} if (view == null) {view = LayoutInflater.from(context).createView(name, "android.widget.", attrs);} if (view == null) {view = LayoutInflater.from(context).createView(name, "android.webkit.", attrs);} }else {view = LayoutInflater.from(context).createView(name, null, attrs);}} catch (Exception e) { L.e("error while create 【" + name + "】 : " + e.getMessage());view = null;}return view;}

if(-1==name.indexOf(’.’)这一句是判断布局中的View是否包含完全路径名。比如TextView,Button等。因此在生成这些View的对象时,需要补全路径。

  • parseSkinAttr
private void parseSkinAttr(Context context, AttributeSet attrs, View view) {List<SkinAttr> viewAttrs = new ArrayList<SkinAttr>();for (int i = 0; i < attrs.getAttributeCount(); i++){String attrName = attrs.getAttributeName(i);String attrValue = attrs.getAttributeValue(i);//判断该attr在换肤时是否支持更换,支持换肤的attr由用户来决定if(!AttrFactory.isSupportedAttr(attrName)){continue;}if(attrValue.startsWith("@")){try {//该attrValue对应的资源idint id = Integer.parseInt(attrValue.substring(1));//该attrValue对应的资源名字String entryName = context.getResources().getResourceEntryName(id);//该attrValue对应的资源类型,比如color,string,drawable等String typeName = context.getResources().getResourceTypeName(id);//根据attrName构造一个SkinAttr对象SkinAttr mSkinAttr = AttrFactory.get(attrName, id, entryName, typeName);if (mSkinAttr != null) {viewAttrs.add(mSkinAttr);}} catch (NumberFormatException e) {e.printStackTrace();} catch (NotFoundException e) {e.printStackTrace();}}}if(!ListUtils.isEmpty(viewAttrs)){SkinItem skinItem = new SkinItem();skinItem.view = view;skinItem.attrs = viewAttrs;mSkinItems.add(skinItem);if(SkinManager.getInstance().isExternalSkin()){//通过SkinAttr的实现类实现换肤功能skinItem.apply();}}}

SkinManager

负责初始化以及切换皮肤。换肤的方法为load(String path,ILoaderListener listener)。

PackageManager mPm = context.getPackageManager();
PackageInfo mInfo = mPm.getPackageArchiveInfo(skinPkgPath, PackageManager.GET_ACTIVITIES);
skinPackageName = mInfo.packageName;AssetManager assetManager = AssetManager.class.newInstance();
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String.class);
addAssetPath.invoke(assetManager, skinPkgPath);Resources superRes = context.getResources();
//重新构造一个Resource对象,这个Resource对象是皮肤包的。
Resources skinResource = new Resources(assetManager,superRes.getDisplayMetrics(),superRes.getConfiguration());

加载皮肤包时,顺便重新构造了皮肤包的对应资源对象Resource。这个可以帮我们获取皮肤包中的资源。以便在主module中动态切换某个资源。 例如:我的项目中在换肤时,要切换支付二维码中间的logo。在SkinManager中新增方法。

//resId是图片在主module中的资源名字。比如:R.drawable.icon_app
public Bitmap getLogo(int resId){Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), resId);if (null!=bitmap&&isDefaultSkin){return bitmap;}//资源的名字。比如:icon_appString resName = context.getResources().getResourceEntryName(resId);//资源在皮肤包中的实际id。这里是通过皮肤包的Resource来获取的。int trueResId = mResources.getIdentifier(resName, "drawable", skinPackageName);bitmap = BitmapFactory.decodeResource(mResources, trueResId);return bitmap;}

自定义SkinAttr

这个开源库对控件的切换支持并不完善,ImageView就不支持切换。我们可以继承SkinAttr来实现对ImageView换肤的支持。

public class ImageAttr extends SkinAttr {@Overridepublic void apply(View view) {if (view instanceof ImageView){if (attrName.equals(IMAGE_SRC)) {((ImageView) view).setImageDrawable(SkinManager.getInstance().getDrawable(attrValueRefId));}}}
}

在AttrFactory中增加对ImageView的支持。

public class AttrFactory {public static final String IMAGE_SRC = "src";public static SkinAttr get(String attrName, int attrValueRefId, String attrValueRefName, String typeName){SkinAttr mSkinAttr = null;if (IMAGE_SRC.equals(attrName)){//生成ImageAttrmSkinAttr = new ImageAttr();}}...}
public static boolean isSupportedAttr(String attrName){return BACKGROUND.equals(attrName) || TEXT_COLOR.equals(attrName)||LIST_SELECTOR.equals(attrName) || DIVIDER.equals(attrName)//支持ImageView||IMAGE_SRC.equals(attrName);}

大家问题有问题可以扣扣扫码

至此本篇结束,祝大家事业顺利!

Android:换肤相关推荐

  1. Android 换肤demo,轻量快捷接入集成,判断是否夜间模式

    true为黑夜模式 //检查当前系统是否已开启暗黑模式 public static boolean getDarkModeStatus(Context context) {int mode = con ...

  2. android换肤哪个简单,Android换肤

    这是一个Android换肤的库,代码量极少,支持换肤的情况还比较多,提供了以下功能: 无需重启,一键换肤效率高 支持App内多套皮肤换肤 支持插件式动态换肤 支持Activity,Fragment,以 ...

  3. android换肤动画,Android换肤(二) — 插件式换肤

    ###前言 上节我们讲到了`Android-skin-support`库的应用内换肤,大家感兴趣的可以参看文章: [Android换肤(一) - 应用内换肤](http://www.demodashi ...

  4. Android-skin-support 一款用心去做的Android 换肤框架

    介绍 Github地址: https://github.com/ximsfei/Android-skin-support Android-skin-support: 一款用心去做的Android 换肤 ...

  5. Android换肤总结

    文章目录 换肤方案 Theme换肤 Resouce换肤 2.拿到皮肤包Resource对象 3.标记需要换肤的View 4.缓存需要换肤的View 5.切换时即时刷新页面 6.制作皮肤包 UiMode ...

  6. Android 换肤之旅——主题切换

    随着手机应用的成熟发展,市面上的应用已不在以简单的实现功能为目标了,它们反而会更加注重用户体验.我们常说的换肤(主题)功能--针对用户的喜好来提供一个可选的主题也是提高用户体验的方式之一.换肤功能不仅 ...

  7. Android 换肤方案详解(一)

    引言 在我们的开发中,也许有些项目会有换肤的需求,这个时候会比较头疼怎么做才能做到一键换肤呢?大家肯定是希望只要一行代码就能调用最好.下面我们先分析一下换肤的本质是什么? 原理 换肤,其本质无非就是更 ...

  8. Android 换肤原理分析

    当了解了一些知识,应该用文字记录它,再抽个时间再看它,永远记住它 Android 换肤的理论知识和文章已经很多了,这里记录一下自己对这块的理解.本文效果如下: 工程:一键换肤的快乐 一.换肤的由来 首 ...

  9. android 换肤框架搭建及使用 (3 完结篇)

    本系列计划3篇: Android 换肤之资源(Resources)加载(一) setContentView() / LayoutInflater源码分析(二) 换肤框架搭建(三) - 本篇 tips: ...

  10. android换肤动画,Android-换肤ThemeSkinning使用

    1.png 2.png 3.png 1.集成步骤: 1.添加依赖 compile 'com.solid.skin:skinlibrary:1.4.3' 参考源码版本 2.让你的 Application ...

最新文章

  1. 斜杆和反斜杠在不同系统的区别
  2. jquery easyui datagrid 分页 详解
  3. hdu 1116 欧拉回路 并查集 一组字符串能否首尾相连成一个字符串
  4. Jmeter添加断言
  5. pip安装软件时出现Command python setup.py egg_info failed with error code 1 in /tmp/pip-build*的解决方案
  6. MNIST机器学习入门
  7. 如何判定选择的日期与当前日期的前后关系
  8. 酒浓码浓 - js / 前端 / 支付宝,微信合并二维码功能
  9. 甲骨文裁员,华大人员瘦身优化,亚马逊鼓励离职?
  10. Ubuntu安装yum
  11. mysql替换后的zzigu_MySQL导入数据报错Got a packet bigger than‘max_allowed_packet’bytes错误的解决方法...
  12. 解压缩报错tar: Error is not recoverable: exiting now
  13. [0.0]-AutoSAR零基础学习-专栏介绍
  14. 特斯拉、谷歌最可怕的对手!乔治·霍兹(George Hotz)
  15. java实现读取文件返回字节数组
  16. vscode 运行C++
  17. 【Java】Java计算器
  18. 1500页技术人的黑皮书 免费下载!
  19. Windows7 X64 虚拟光驱
  20. github上的图片看不了,教你解决

热门文章

  1. cloudflare部署php,CloudFlare加速WordPress:页面规则配置
  2. 计算机调整最佳外观不起作用,Win7怎么设置性能选项视觉效果调整为最佳外观?...
  3. 贝儿的伤感爱情空间日志发布:宝贝,快回头吧,我等你
  4. 2022云栖现场|体验阿里巴巴工作数字化实践
  5. Smarty教程[5]
  6. html中的if函数,countif函数的使用方法
  7. CBA京沪大战加时决胜 更有趣的是胜负之外的细节
  8. 2019年春季面试题
  9. Dell一体机通过U盘安装Debian
  10. 【广告算法工程师入门 14】机制设计-带有保留价的拍卖与最优保留价