完整脑图: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,就能走ActivityonCreateView()回调,来对控件属性做操作。

用归纳法,见下图:


最后,也就剩下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

网易换肤第一篇:换肤技术解密!
网易换肤第二篇:本地换肤实现!
网易换肤第三篇:动态换肤实现!

架构师系列文章一览

网易换肤第二篇:本地换肤实现!相关推荐

  1. 网易换肤第一篇:换肤技术解密!

    参考 脑图:https://note.youdao.com/s/Q1e6r39j 最终效果: Demo源码:点击跳转 技术点分析 换肤的核心思路主要是在setContentView()之前调用setF ...

  2. C语言初阶第二篇:换我心,为你心,始知C语言 从程序调试到全网最详细字符串教学来喽

    换我心,为你心,始知C语言 老铁们,我们这个系列的上一篇文章讲到了变量常量,那么今天我们就继续说一说之后的一些知识框架,保证零基础也能看懂,废话不多说,我们开始吧! 第二篇 一.探讨字符串 二.插入知 ...

  3. Android换肤功能实现与换肤框架QSkinLoader使用方式介绍

    框架地址:https://github.com/qqliu10u/QSkinLoader 效果图 https://github.com/qqliu10u/QSkinLoader/raw/master/ ...

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

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

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

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

  6. android换肤动画,Android动态换肤框架-实现换肤

    1.换肤流程 1 2.采集流程 2 3.Android资源查找流程 3 4.采集需要换肤的控件 换肤我们需要换所有可能需要换的页面控件,所以我们不可能在每个页面重新findviewById,这时就需要 ...

  7. 红蓝对抗之隧道技术第二篇(reGeorg内网穿透、SSH隧道本地Socks代理、SSH远程转发、Earthworm Socks5代理、Tunna正向代理、ICMP隧道、DNS隧道、Frp穿透)

    文章目录 隧道应用 reGeorg内网穿透 SSH本地转发(正向) 场景一 场景二 SSH远程转发(反向) Earthworm Socks5代理 正向代理 反向代理 Tunna正向代理 ICMP隧道 ...

  8. Vue移动网页开发调试过程(第二篇)——weinre

    目录 工具介绍:weinre 安装开启服务 连接项目开始调试 前言:前面一篇主要讲的是视图层面上的同步预览效果,这一篇的内容涉及就是像PC端一样调试css和js以及捕捉接口请求 工具介绍:weinre ...

  9. 吃灰树莓派第二篇|不设置主从机,看ROS2如何进行多机通信

    小鱼吃灰树莓派第二篇|不设置主从机,看ROS2如何进行多机通信 吃灰小鱼 一.ROS2多机通信实践 1.1 让树莓派和电脑连接在同一个局域网 1.2 使用ros2的多播(multicast)指令测试 ...

最新文章

  1. R语言vtreat包自动处理dataframe的缺失值并生成对应的数据列_isbad来指示数据的原始缺失情况、查看特定字段缺失的那些数据行、查看数据集中多个字段的均值
  2. Jetty在win10上的配置,IDEA中配置Jetty,Maven中配置Jetty插件,Eclipse中配置Jetty插件及其使用,通过java代码内嵌Jetty Server
  3. Android之提示This version of Android Studio cannot open this project, please retry with Android Studio
  4. Libusb开发教程一 安装
  5. 如何截取_【实用技巧】如何截取网页长图
  6. 2018年3月计算机二级考试题,2018年3月计算机二级考试Access综合试题十
  7. IBM Watson IoT
  8. atitit.hbnt orm db 新新增更新最佳实践o99
  9. BackgroundWorker 简单使用教程 多个线程的创建
  10. process下载与使用
  11. 【linux学习】yum提示 :Another app is currently holding the yum lock; waiting for it to exit...
  12. 推荐几款拍证件照的小程序
  13. macOSX下格式化外接固态硬盘
  14. Linux 下修改时间和时区
  15. 图标.svg html怎么换颜色,如何改变SVG图像的颜色使用CSS(jQuery SVG图像替换)?
  16. 实时摄像头直播方案 RTSP
  17. C语言三个点“...“符号含义之数组批量赋相同值
  18. php html 纯文本,php – 使用纯文本回退发送HTML简报
  19. Qt学习笔记(五):菜单栏
  20. 区块链能够应用在哪些行业

热门文章

  1. js和html:周岁年龄计算器
  2. Js年龄计算,精确到岁、月、小时
  3. FFmpeg 音视频截取
  4. 小清新版js扫雷(使用原生js)
  5. linux具有写保护权限的文件,细说Linux权限
  6. 基于opencv实现人脸识别及签到系统
  7. 人脸识别签到实习第三天
  8. saas平台相关内容
  9. PAT_乙级_1001_筱筱
  10. 分支-07. 比较大小(10)