网易换肤第二篇:本地换肤实现!
完整脑图:https://note.youdao.com/s/V2csJmYS
Demo源码:点击下载
技术分析
我们在换肤的第一篇介绍了换肤的核心思想。就是在setContentView()
之前调用setFactory2()
。
第一篇的Demo利用的是AOP切面方法registerActivityLifecycleCallbacks(xxx)
回调在setContentView()
之前,从而在registerActivityLifecycleCallbacks的onActivityCreated()
方法中设置Factory。如此就能拦截到控件的属性,根据拦截到的控件的属性,重新赋值控件的textColor、background等属性,从而实现换肤的。
本Demo的实现,主要基于以下两个狙击点。
1、super.onCreate(savedInstanceState)方法
2、Activity实现了Factory接口
前面说过,只要在setContentView()之前setFactory2()就行。super.onCreate(savedInstanceState)
方法就是在setContentView()方法之前执行的。
一直跟踪super.onCreate(savedInstanceState)方法,最终会发现setFactory的逻辑,如下:
AppCompatDelegateImpl.java(1008)
public void installViewFactory() {LayoutInflater layoutInflater = LayoutInflater.from(this.mContext);if (layoutInflater.getFactory() == null) {LayoutInflaterCompat.setFactory2(layoutInflater, this);} else if (!(layoutInflater.getFactory2() instanceof AppCompatDelegateImpl)) {Log.i("AppCompatDelegate", "The Activity's LayoutInflater already has a Factory installed so we can not install AppCompat's");}
}
它这里传了this
,可以预见AppCompatDelegateImpl
是实现了Factory接口的,最后会通过AppCompatDelegateImpl自身的onCreateView()方法创建的View。
onCreateView()中如何创建的View的,下面再看源码,先知道是通过AppCompatViewInflater
来做控件的具体初始化的。
第一个狙击点可以抽出下图内容:
细心地同学肯定注意到了AppCompatDelegateImpl的installViewFactory()方法中,只有当layoutInflater.getFactory() == null
的时候,才会去setFactory。
也就是说我在super.onCreate(savedInstanceState)之前,先给它setFactory就能走自己Factory的onCreateView()回调。
换肤第一篇中我们是自己去实现Factory2接口,在本例中,就用到了我们第二个狙击点。
Activity实现了Factory接口!!!
也就是说,只要我们在super.onCreate(savedInstanceState)之前,setFactory的时候,传this,就能走Activity
的onCreateView()回调,来对控件属性做操作。
用归纳法,见下图:
最后,也就剩下Activity的onCreateView()中的回调怎么实现了。
直接模拟super.onCreate(savedInstanceState)中AppCompatViewInflater类中的实现就好了。
参考代码:
/*** 自定义控件加载器(可以考虑该类不被继承)*/
public final class CustomAppCompatViewInflater extends AppCompatViewInflater {private String name; // 控件名private Context context; // 上下文private AttributeSet attrs; // 某控件对应所有属性public CustomAppCompatViewInflater(@NonNull Context context) {this.context = context;}public void setName(String name) {this.name = name;}public void setAttrs(AttributeSet attrs) {this.attrs = attrs;}/*** @return 自动匹配控件名,并初始化控件对象*/public View autoMatch() {View view = null;switch (name) {case "LinearLayout":// view = super.createTextView(context, attrs); // 源码写法view = new SkinnableLinearLayout(context, attrs);this.verifyNotNull(view, name);break;case "RelativeLayout":view = new SkinnableRelativeLayout(context, attrs);this.verifyNotNull(view, name);break;case "TextView":view = new SkinnableTextView(context, attrs);this.verifyNotNull(view, name);break;case "ImageView":view = new SkinnableImageView(context, attrs);this.verifyNotNull(view, name);break;case "Button":view = new SkinnableButton(context, attrs);this.verifyNotNull(view, name);break;}return view;}/*** 校验控件不为空(源码方法,由于private修饰,只能复制过来了。为了代码健壮,可有可无)** @param view 被校验控件,如:AppCompatTextView extends TextView(v7兼容包,兼容是重点!!!)* @param name 控件名,如:"ImageView"*/private void verifyNotNull(View view, String name) {if (view == null) {throw new IllegalStateException(this.getClass().getName() + " asked to inflate view for <" + name + ">, but returned null");}}
}
详细实现就参考Demo吧,思路其实很简单,只是会有对setFactory这块逻辑的流程不了解的。建议跟踪着点几遍源码。
About
网易换肤第一篇:换肤技术解密!
网易换肤第二篇:本地换肤实现!
网易换肤第三篇:动态换肤实现!
架构师系列文章一览
网易换肤第二篇:本地换肤实现!相关推荐
- 网易换肤第一篇:换肤技术解密!
参考 脑图:https://note.youdao.com/s/Q1e6r39j 最终效果: Demo源码:点击跳转 技术点分析 换肤的核心思路主要是在setContentView()之前调用setF ...
- C语言初阶第二篇:换我心,为你心,始知C语言 从程序调试到全网最详细字符串教学来喽
换我心,为你心,始知C语言 老铁们,我们这个系列的上一篇文章讲到了变量常量,那么今天我们就继续说一说之后的一些知识框架,保证零基础也能看懂,废话不多说,我们开始吧! 第二篇 一.探讨字符串 二.插入知 ...
- Android换肤功能实现与换肤框架QSkinLoader使用方式介绍
框架地址:https://github.com/qqliu10u/QSkinLoader 效果图 https://github.com/qqliu10u/QSkinLoader/raw/master/ ...
- android换肤哪个简单,Android换肤
这是一个Android换肤的库,代码量极少,支持换肤的情况还比较多,提供了以下功能: 无需重启,一键换肤效率高 支持App内多套皮肤换肤 支持插件式动态换肤 支持Activity,Fragment,以 ...
- android换肤动画,Android换肤(二) — 插件式换肤
###前言 上节我们讲到了`Android-skin-support`库的应用内换肤,大家感兴趣的可以参看文章: [Android换肤(一) - 应用内换肤](http://www.demodashi ...
- android换肤动画,Android动态换肤框架-实现换肤
1.换肤流程 1 2.采集流程 2 3.Android资源查找流程 3 4.采集需要换肤的控件 换肤我们需要换所有可能需要换的页面控件,所以我们不可能在每个页面重新findviewById,这时就需要 ...
- 红蓝对抗之隧道技术第二篇(reGeorg内网穿透、SSH隧道本地Socks代理、SSH远程转发、Earthworm Socks5代理、Tunna正向代理、ICMP隧道、DNS隧道、Frp穿透)
文章目录 隧道应用 reGeorg内网穿透 SSH本地转发(正向) 场景一 场景二 SSH远程转发(反向) Earthworm Socks5代理 正向代理 反向代理 Tunna正向代理 ICMP隧道 ...
- Vue移动网页开发调试过程(第二篇)——weinre
目录 工具介绍:weinre 安装开启服务 连接项目开始调试 前言:前面一篇主要讲的是视图层面上的同步预览效果,这一篇的内容涉及就是像PC端一样调试css和js以及捕捉接口请求 工具介绍:weinre ...
- 吃灰树莓派第二篇|不设置主从机,看ROS2如何进行多机通信
小鱼吃灰树莓派第二篇|不设置主从机,看ROS2如何进行多机通信 吃灰小鱼 一.ROS2多机通信实践 1.1 让树莓派和电脑连接在同一个局域网 1.2 使用ros2的多播(multicast)指令测试 ...
最新文章
- R语言vtreat包自动处理dataframe的缺失值并生成对应的数据列_isbad来指示数据的原始缺失情况、查看特定字段缺失的那些数据行、查看数据集中多个字段的均值
- Jetty在win10上的配置,IDEA中配置Jetty,Maven中配置Jetty插件,Eclipse中配置Jetty插件及其使用,通过java代码内嵌Jetty Server
- Android之提示This version of Android Studio cannot open this project, please retry with Android Studio
- Libusb开发教程一 安装
- 如何截取_【实用技巧】如何截取网页长图
- 2018年3月计算机二级考试题,2018年3月计算机二级考试Access综合试题十
- IBM Watson IoT
- atitit.hbnt orm db 新新增更新最佳实践o99
- BackgroundWorker 简单使用教程 多个线程的创建
- process下载与使用
- 【linux学习】yum提示 :Another app is currently holding the yum lock; waiting for it to exit...
- 推荐几款拍证件照的小程序
- macOSX下格式化外接固态硬盘
- Linux 下修改时间和时区
- 图标.svg html怎么换颜色,如何改变SVG图像的颜色使用CSS(jQuery SVG图像替换)?
- 实时摄像头直播方案 RTSP
- C语言三个点“...“符号含义之数组批量赋相同值
- php html 纯文本,php – 使用纯文本回退发送HTML简报
- Qt学习笔记(五):菜单栏
- 区块链能够应用在哪些行业