android-自定义换肤(2)
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)相关推荐
- android 换肤 视频,网易云音乐4.0版体验:自定义换肤和短视频来了
原标题:网易云音乐4.0版体验:自定义换肤和短视频来了 日前,网易云音乐的iOS和Android更新到了4.0版本,对于期待更多创新功能的忠实粉来说,这着实是一个好消息.在新版本到来之后,不少人开始发 ...
- Android主题换肤实现
本系列文章主要是对一个Material Design的APP的深度解析,主要包括以下内容 基于Material Design Support Library作为项目整体框架.对应博文:Android ...
- android换肤的实现方案,Android应用开发之Android一键换肤功能实现
本文将带你了解Android应用开发之Android一键换肤功能实现,希望本文对大家学Android有所帮助. < 市面上对数的App都提供换肤功能,这里暂且不讲白天和夜间模式 下图是网易云音乐 ...
- Android App 换肤实现方式
Android App 换肤的引入意味着给用户提供不同的界面样式,以适应不同用户的审美需求.引入换肤可以让用户更加个性化地使用 App,增强用户对 App 的黏度和使用体验. Android App ...
- miui主题风格_一种android系统换肤功能的设计,董红光:MIUI主题风格.pdf
MIUI主题风格主题风格主题风格主题风格 一种Android系统换肤功能的设计思路 董红光 2/29/2012 "主题"是什么 ? Symbian的"主题" A ...
- Android主题换肤 无缝切换
作者 _SOLID 关注 2016.04.17 22:04* 字数 4291 阅读 23224评论 123喜欢 679 今天再给大家带来一篇干货. Android的主题换肤 ,可插件化提供皮肤包,无需 ...
- Android 主题换肤的开源库
Android 主题换肤的开源库(插件化换肤) 新增夜间模式的简洁实现方式,不需要再去单独创建一个皮肤包(目前处于beta版本) 夜间模式实现方式 前提条件还是每个使用到的资源必须是引用的,不能是具体 ...
- Android主题换肤 无缝切换 你值得拥有
链接:https://www.jianshu.com/p/af7c0585dd5b 天再给大家带来一篇干货. Android的主题换肤 ,可插件化提供皮肤包,无需Activity的重启直接实现无缝切换 ...
- android+qq换肤实现,QMUI 换肤 · Tencent/QMUI_Android Wiki · GitHub
QMUI版本要求: v2.0.0-alpha05+ 官方 Android 10 Dark Mode 适配方案 Android 10 提供了 Dark Mode 适配提供的 API: 提供了 value ...
- 【Android】换肤技术讲解
主题,是许多APP必备的一个功能,用户可以根据自己的喜好,来切换具有个性的主题,同时能让我们的APP更具把玩性.这篇博文就来聊聊皮肤切换的原理,效果图如下: 这里为了便于理解,在换肤的时候,只是简单切 ...
最新文章
- plc和pc串口通讯接线_让你搞懂PLC串口通讯和通讯接口,这东西估计没几个能说清楚~...
- linux定时备份mysql_linux定时备份MySQL数据库并删除七天前的备份文件
- 美国大学计算机专业排名2014,2014年美国大学计算机科学专业排名
- SerializeUtil 序列化 java
- 机械革命深海泰坦X1(1050T)触控板用不了三指
- 空间滤波_空间频率 、 光波方向 与 空间滤波 4f系统
- 学习笔记-数据结构与算法之线性表
- 揭秘React同构应用
- 清除Eclipse工作空间列表
- vc 模拟按键 模拟windows消息方式_PC微信电脑端WeChat点击脚本(按键精灵)2020小工具...
- kodi教程_KODI添加电视直播+修改台标教程
- 【Linux学习】Vim 怎么设置显示行号以及永久性显示行号
- 我的孤独自学之路----kali 安装及更新源
- 学习自我管理和自我营销
- 22一战上岸首师大电子信息经验分享|低成本获得大收益|电子信息
- PTA 7-13 列车调度 (25 分) C语言和C++实现(二分查找)
- 【Codecs系列】X264码率控制总结1——ABR,CQP,CRF
- 【Java面试】什么是字节码?采用字节码的好处是什么?
- 文件md5加密基本操作
- (zt)魔方玩法(图解)