android-自定义换肤(1) https://blog.csdn.net/tiangaopan/article/details/104895134

前面说到本质上是通过实现LayoutInflater.Factory2,在onCreateView()方法中进行处理

这就需要我们找到对应的控件,控件又有系统控件和自定义控件,这里我们先说系统控件,自定义后面会说处理方法

通过看源码,我们可以找到系统控件在这三个包下,

private static final String[] mClassPrefixList = {"android.widget.", "android.view.", "android.webkit.",}private static final Class[] mConstructorSignature = new Class[]{Context.class, AttributeSet.class};
private static final HashMap<String, Constructor<? extends View>> mConstructorMap = new HashMap<>();
 @Overridepublic View onCreateView(View parent, String name, Context context, AttributeSet attrs) {//反射获取View view = createViewFromTag(name, context, attrs);//自定义viewif (view == null) {view = createView(name, context, attrs);}//筛选符合属性的attrsmSkinAttribute.load(view, attrs);return view;}
 private View createViewFromTag(String name, Context context, AttributeSet attrs) {//包含自定义控件if (-1 != name.indexOf(".")) {return null;}View view = null;for (String s : mClassPrefixList) {//mClassPrefixList[i] + name 组成全类名view = createView(s + name, context, attrs);if (view != null) {break;}}return view;}
private View createView(String name, Context context, AttributeSet attrs) {Constructor<? extends View> constructor = mConstructorMap.get(name);if (constructor == null) {try {//通过全类名拿到classClass<? extends View> aClass = context.getClassLoader().loadClass(name).asSubclass(View.class);//获取构造方法constructor = aClass.getConstructor(mConstructorSignature);mConstructorMap.put(name, constructor);} catch (Exception e) {e.printStackTrace();}}if (constructor != null) {try {return constructor.newInstance(context, attrs);} catch (Exception e) {e.printStackTrace();}}return null;}

对于已经通过反射获取到构造方法的,可以使用map进行存储,没必要每次都进行一次获取

public class SkinAttribute {private static final List<String> sAttribute = new ArrayList<>();static {sAttribute.add("background");sAttribute.add("src");sAttribute.add("textColor");sAttribute.add("drawableLeft");sAttribute.add("drawableTop");sAttribute.add("drawableRight");sAttribute.add("drawableBottom");}private List<SkinView> skinViews = new ArrayList<>();private Typeface mSkinTypeface;public SkinAttribute(Typeface skinTypeface) {mSkinTypeface = skinTypeface;}public void load(View view, AttributeSet attrs) {List<SkinPain> skinPains = new ArrayList<>();int attributeCount = attrs.getAttributeCount();for (int i = 0; i < attributeCount; i++) {//获取属性名字String attributeName = attrs.getAttributeName(i);if (sAttribute.contains(attributeName)) {//获取属性值String attributeValue = attrs.getAttributeValue(i);//写死的情况if (attributeValue.startsWith("#")) {continue;}int resId;//系统自带的 attributeValue = "?1313123"if (attributeValue.startsWith("?")) {int attrId = Integer.parseInt(attributeValue.substring(1));//存在数组的可能resId = SkinThemeUtils.getResId(view.getContext(), new int[]{attrId})[0];} else {//attributeValue = "@21321213"resId = Integer.parseInt(attributeValue.substring(1));}if (resId != 0) {SkinPain skinPain = new SkinPain(attributeName, resId);skinPains.add(skinPain);}}}if (!skinPains.isEmpty() || view instanceof TextView || view instanceof SkinViewSupport) {SkinView skinView = new SkinView(view, skinPains);skinView.applySkin(mSkinTypeface);skinViews.add(skinView);}}public void applySkin() {for (SkinView skinView : skinViews) {skinView.applySkin(mSkinTypeface);}}public void setTypeface(Typeface skinTypeface) {this.mSkinTypeface = skinTypeface;}static class SkinView {View view;List<SkinPain> skinPains;public SkinView(View view, List<SkinPain> skinPains) {this.view = view;this.skinPains = skinPains;}public void applySkin(Typeface typeface) {applyTypeface(typeface);applySkinSupport();for (SkinPain skinPair : skinPains) {Drawable left = null, top = null, right = null, bottom = null;switch (skinPair.attributeName) {case "background":Object background = SkinResources.getInstance().getBackground(skinPair.resId);//Colorif (background instanceof Integer) {view.setBackgroundColor((Integer) background);} else {ViewCompat.setBackground(view, (Drawable) background);}break;case "src":background = SkinResources.getInstance().getBackground(skinPair.resId);if (background instanceof Integer) {((ImageView) view).setImageDrawable(new ColorDrawable((Integer)background));} else {((ImageView) view).setImageDrawable((Drawable) background);}break;case "textColor":((TextView) view).setTextColor(SkinResources.getInstance().getColorStateList(skinPair.resId));break;case "drawableLeft":left = SkinResources.getInstance().getDrawable(skinPair.resId);break;case "drawableTop":top = SkinResources.getInstance().getDrawable(skinPair.resId);break;case "drawableRight":right = SkinResources.getInstance().getDrawable(skinPair.resId);break;case "drawableBottom":bottom = SkinResources.getInstance().getDrawable(skinPair.resId);break;default:break;}if (null != left || null != right || null != top || null != bottom) {((TextView) view).setCompoundDrawablesWithIntrinsicBounds(left, top, right,bottom);}}}//自定义view替换private void applySkinSupport() {if (view instanceof SkinViewSupport) {((SkinViewSupport) view).applySkin();}}//字体换肤private void applyTypeface(Typeface typeface) {if (view instanceof TextView) {((TextView) view).setTypeface(typeface);}}}static class SkinPain {String attributeName;int resId;public SkinPain(String attributeName, int resId) {this.attributeName = attributeName;this.resId = resId;}}
}

通过attributeName来知道是background,textcolor等属性,通过resid可以知道是资源名,这时候去获取资源包中的对应resid,即可实现相应的换肤

还有个需要注意的是,每个界面都应该进行相应的换肤,所以我们需要去实现Application.ActivityLifecycleCallbacks,这里去进行加载和移除

@Overridepublic void onActivityCreated(Activity activity, Bundle savedInstanceState) {//更新状态栏SkinThemeUtils.updateStatusBarColor(activity);//更新字体Typeface skinTypeface = SkinThemeUtils.getSkinTypeface(activity);try {LayoutInflater layoutInflater = LayoutInflater.from(activity);Field mFactorySet = LayoutInflater.class.getDeclaredField("mFactorySet");mFactorySet.setAccessible(true);mFactorySet.setBoolean(layoutInflater, false);SkinLayoutFactory skinLayoutFactory = new SkinLayoutFactory(activity, skinTypeface);//添加自定义创建view 工厂layoutInflater.setFactory2(skinLayoutFactory);//注册观察者SkinManager.getInstance().addObserver(skinLayoutFactory);mFactoryMap.put(activity, skinLayoutFactory);} catch (Exception e) {e.printStackTrace();}}

在这里我们对对应的setFactory2进行替换,有个需要注意的是mFactorySet这个属性,如果是true的话会抛出异常,同时该变量是私有的,所以我们需要通过反射找到该变量将其更改为false

 public void setFactory2(Factory2 factory) {if (mFactorySet) {throw new IllegalStateException("A factory has already been set on this LayoutInflater");}if (factory == null) {throw new NullPointerException("Given factory can not be null");}mFactorySet = true;if (mFactory == null) {mFactory = mFactory2 = factory;} else {mFactory = mFactory2 = new FactoryMerger(factory, factory, mFactory, mFactory2);}}

android-自定义换肤(2)相关推荐

  1. android 换肤 视频,网易云音乐4.0版体验:自定义换肤和短视频来了

    原标题:网易云音乐4.0版体验:自定义换肤和短视频来了 日前,网易云音乐的iOS和Android更新到了4.0版本,对于期待更多创新功能的忠实粉来说,这着实是一个好消息.在新版本到来之后,不少人开始发 ...

  2. Android主题换肤实现

    本系列文章主要是对一个Material Design的APP的深度解析,主要包括以下内容 基于Material Design Support Library作为项目整体框架.对应博文:Android ...

  3. android换肤的实现方案,Android应用开发之Android一键换肤功能实现

    本文将带你了解Android应用开发之Android一键换肤功能实现,希望本文对大家学Android有所帮助. < 市面上对数的App都提供换肤功能,这里暂且不讲白天和夜间模式 下图是网易云音乐 ...

  4. Android App 换肤实现方式

    Android App 换肤的引入意味着给用户提供不同的界面样式,以适应不同用户的审美需求.引入换肤可以让用户更加个性化地使用 App,增强用户对 App 的黏度和使用体验. Android App ...

  5. miui主题风格_一种android系统换肤功能的设计,董红光:MIUI主题风格.pdf

    MIUI主题风格主题风格主题风格主题风格 一种Android系统换肤功能的设计思路 董红光 2/29/2012 "主题"是什么 ? Symbian的"主题" A ...

  6. Android主题换肤 无缝切换

    作者 _SOLID 关注 2016.04.17 22:04* 字数 4291 阅读 23224评论 123喜欢 679 今天再给大家带来一篇干货. Android的主题换肤 ,可插件化提供皮肤包,无需 ...

  7. Android 主题换肤的开源库

    Android 主题换肤的开源库(插件化换肤) 新增夜间模式的简洁实现方式,不需要再去单独创建一个皮肤包(目前处于beta版本) 夜间模式实现方式 前提条件还是每个使用到的资源必须是引用的,不能是具体 ...

  8. Android主题换肤 无缝切换 你值得拥有

    链接:https://www.jianshu.com/p/af7c0585dd5b 天再给大家带来一篇干货. Android的主题换肤 ,可插件化提供皮肤包,无需Activity的重启直接实现无缝切换 ...

  9. android+qq换肤实现,QMUI 换肤 · Tencent/QMUI_Android Wiki · GitHub

    QMUI版本要求: v2.0.0-alpha05+ 官方 Android 10 Dark Mode 适配方案 Android 10 提供了 Dark Mode 适配提供的 API: 提供了 value ...

  10. 【Android】换肤技术讲解

    主题,是许多APP必备的一个功能,用户可以根据自己的喜好,来切换具有个性的主题,同时能让我们的APP更具把玩性.这篇博文就来聊聊皮肤切换的原理,效果图如下: 这里为了便于理解,在换肤的时候,只是简单切 ...

最新文章

  1. plc和pc串口通讯接线_让你搞懂PLC串口通讯和通讯接口,这东西估计没几个能说清楚~...
  2. linux定时备份mysql_linux定时备份MySQL数据库并删除七天前的备份文件
  3. 美国大学计算机专业排名2014,2014年美国大学计算机科学专业排名
  4. SerializeUtil 序列化 java
  5. 机械革命深海泰坦X1(1050T)触控板用不了三指
  6. 空间滤波_空间频率 、 光波方向 与 空间滤波 4f系统
  7. 学习笔记-数据结构与算法之线性表
  8. 揭秘React同构应用
  9. 清除Eclipse工作空间列表
  10. vc 模拟按键 模拟windows消息方式_PC微信电脑端WeChat点击脚本(按键精灵)2020小工具...
  11. kodi教程_KODI添加电视直播+修改台标教程
  12. 【Linux学习】Vim 怎么设置显示行号以及永久性显示行号
  13. 我的孤独自学之路----kali 安装及更新源
  14. 学习自我管理和自我营销
  15. 22一战上岸首师大电子信息经验分享|低成本获得大收益|电子信息
  16. PTA 7-13 列车调度 (25 分) C语言和C++实现(二分查找)
  17. 【Codecs系列】X264码率控制总结1——ABR,CQP,CRF
  18. 【Java面试】什么是字节码?采用字节码的好处是什么?
  19. 文件md5加密基本操作
  20. (zt)魔方玩法(图解)

热门文章

  1. 自己动手编译Mozilla Firefox和ThunderBird
  2. 为组件添加Expires头,最大化利用浏览器缓存
  3. 中小企业也有机会!物联网的战场硝烟
  4. 嵌入式接口技术(一)GPIO
  5. 怎么用程序设置NVIDIA双显卡环境变量
  6. Python 海象运算符 详细介绍
  7. CANoe常用操作(CANoe系列其一)
  8. 液压装置状态评估数据集
  9. MVCC下的RR和RC级别的区别和实现
  10. tensorboard ckpt pb 模型的输出节点_乐学初中数学研究|第七期:最值问题之费马点模型...