查看原文
其他

Android 两行代码实现换肤 从appcompat-v7原理出发

ximsfei 鸿洋 2019-04-05

每日推荐


推荐一个文件选择库,支持文件、图片多选,已经图片查看等。



项目地址:

https://github.com/fishwjy/MultiType-FilePicker


本文作者


本文由ximsfei投稿。

ximsfei的博客地址:

http://blog.csdn.net/ximsfei


1

背景


换肤方案原理在网上已经很多了, 这里不再详细描述, 强迫症的我总是想让提供给别人使用的SDK尽量好用, 哪怕是给自己带来额外的工作量, 经过一段时间的奋斗, 实现了一个自我感觉良好的换肤框架.


这里主要来看看Android 源码中”com.android.support:appcompat-v7”包的实现, 以及源码思想在Android-skin-support中的应用 – 如何打造一款好用的换肤框架.


2

appcompat-v7包实现


首先来看一下源码的实现: 


AppCompatActivity源码



AppCompatActivity 将大部分生命周期委托给了AppCompatDelegate。

看下AppCompateDelegate的子类AppCompatDelegateImplV9实现:



从这可以看出通过实现LayoutInflaterFactory接口来实现换肤至少可以支持到api 9以上。


网上很多换肤框架的实现, 通过LayoutInflater.setFactory的方式, 在回调的onCreateView中解析每一个View的attrs, 判断是否有已标记需要换肤的属性, 比方说background, textColor, 或者说相应资源是否为skin_开头等等. 


然后保存到map中, 对每一个View做for循环去遍历所有的attr, 想要对更多的属性进行换肤, 需要Activity实现接口, 将需要换肤的View, 以及相应的属性收集到一起。


那么是不是能够寻求一种让使用者更方便的方式来实现, 做一个侵入性尽量小的框架呢?


本着开发者应有的好奇心, 深入的研究了一些v7包的实现:

AppCompatDelegateImplV9中, 在LayoutInflaterFactory的接口方法

onCreateView 中将View的创建交给了AppCompatViewInflater



再来看一下AppCompatViewInflater中createView的实现



再看一下其中一个类AppCompatTextView的实现



AppCompatBackgroundHelper.Java



到这里我仿佛是发现了新大陆一样兴奋, 源码中可以通过拦截View创建过程, 替换一些基础的组件, 然后对一些特殊的属性(eg: background, textColor) 做处理, 那我们为什么不能将这种思想拿到换肤框架中来使用呢?


3

Android-skin-support换肤框架实现


抱着试一试不会少块肉的心情, 开始了我的换肤框架开发之路。


先简单讲一下原理: 


1. 参照源码实现在Activity onCreate中为LayoutInflater setFactory, 将View的创建过程交给自定义的SkinCompatViewInflater类来实现

2. 重写系统组件, 实现换肤接口, 表明该控件支持换肤, 并在View创建之后统一收集 

3. 在重写的View中解析出需要换肤的属性, 并保存ResId到成员变量 

4. 重写类似setBackgroundResource方法, 解析需要换肤的属性, 并保存变量 

5. applySkin 在切换皮肤的时候, 从皮肤资源中获取资源


下面说一个简单的例子(SkinCompatTextView): 


1. 实现SkinCompatSupportable接口 
2. 在构造方法中通过SkinCompatBackgroundHelper和SkinCompatTextHelper分别解析出background, textColor并保存 
3. 重写setBackgroundResource和setTextAppearance, 解析出对应的资源Id, 表明该控件支持从代码中设置资源, 且支持该资源换肤 
4. 在用户点击切换皮肤时调用applySkin方法设置皮肤



对于其他View也一样,复写其换肤相关的API,统一对外入口为applySkin。


SkinCompatTextHelper的部分实现:


4

开发过程中遇到的一些问题


在5.0以上, 使用color为ImageView设置src, 可以通过getColorStateList获取资源, 而在5.0以下, 需要通过ColorDrawable setColor的方式实现



还有很多问题, 有兴趣的同学可以来一起交流解决.


5
总结


这样的做法与网上其他框架相比优势在哪里, 为什么重复造轮子?


  • 在增加框架开发成本的基础上降低了框架使用的成本, 我觉得更有意义, 一次开发, 所有Android 开发者都受用;

  • 换肤框架对业务代码的侵入性比较小, 业务代码只需要继承自SkinCompatActivity, 不需要实现接口重写方法, 不需要其他额外的代码, 接入方便, 假如将来不想再使用本框架, 只需要把SkinCompatActivity改为原生Activity即可;

  • 深入源码, 和源码实现方式类似, 兼容性更好.


为什么选择继承自AppCompatActivity, AppCompatTextView…而不是选择直接继承自Activity, TextView…


  • 本身appcompat-v7包是一个support包, 兼容原生控件, 同时符合Material design, 我们只需要获取我们想要换肤的属性就可以在不破坏support包属性的前提下进行换肤;

  • 参与开发的同学更多的话, 完全可以支持一套继承自Activity, TextView…的skin support包.


自定义View能否支持, 第三方控件是否支持换肤


  • 答案是肯定的, 完全可以参照SkinCompatTextView的实现, 自己去实现自定义控件, 对于使用者来说, 扩展性很好.


开源地址:

https://github.com/ximsfei/Android-skin-support

ZZS

优秀人才不缺工作机会,只缺适合自己的好机会。但是他们往往没有精力从海量机会中找到最适合的那个。

100offer 会对平台上的人才和企业进行严格筛选,让「最好的人才」和「最好的公司」相遇。

扫描下方二维码,注册 100offer,谈谈你对下一份工作的期待。一周内,收到 5-10 个满足你要求的好机会!



如果你有想学习的文章直接留言,我会整理征稿。如果你有好的文章想和大家分享欢迎投稿,直接向我投递文章链接即可。


欢迎长按下图->识别图中二维码或者扫一扫关注我的公众号:

    您可能也对以下帖子感兴趣

    文章有问题?点此查看未经处理的缓存