如何用10行代码让app全局置灰

内容概要

前段时间因为新冠肺炎特别严重,政府规定今年的4月4号为悼念日,全部互联网项目能置灰的要跟随置灰处理。咱们能够看到在京东、百度等部分app中都有置灰的功能。若是是在网页上的话,只须要一句代码就能够搞定了,可是app里实现可能有些同窗会感受迷茫。今天笔者也跟上潮流,给你们分享一篇如何在app中实现全局置灰吧,没有这个需求的朋友们也能够学习探讨一下思路,但愿能够帮到你们!程序员

一 、如何实现页面灰度化

实现灰度化的思路应该从Paint出发,由于系统是经过Paint将内容绘制到界面上的,若是能找到Paint相关的设置方法,那就再也合适不过了。自定义View作得多的同窗可能知道Paint中能够设置ColorMatrix,如下是其源码,从源码的注释咱们能够看到若是将setSaturation的sat参数设置为0就表明灰度模式。app


OK,说干就干,咱们自定义一个图片控件,修改其Paint中的Matrix属性试试。首先初始化ColorMatrix对象,而后按照源码中的说法,将setSaturation参数的值设置为0,接下来将该ColorMatrix设置到Paint中,最后再onDraw方法中使用刚刚的Paint对象,代码大体以下:布局


以上是在ImageView中实现灰度化,那么文本的TextView其实也是同样的,由于本质都是使用Paint进行的绘制,因此能够直接将上面代码拷贝到自定义TextView中。post

接下来咱们在页面中使用上面自定义好的2个View,咱们运行一下,看一下效果。不出因此然,文本和图片都变成了灰色,具体运行结果以下图:学习


效果是实现了,又有一个新的问题摆在眼前。在项目里数以万计的ImageView和TextView,总不可能一个一个来替换吧,这样作要换到猴年马月去?spa

2、如何提高替换效率

从上面TextView、ImageView两者的置灰实现没有任何区别,咱们能够猜想是否是全部的View都能给置灰呢,那么ViewGroup做为一个特殊的View是否能够置灰呢?接下来咱们来验证一下这个猜测,自定义一个RelativeLayout,仍是以前的代码,须要注意的是这里须要复写dispatchDraw方法。关于onDraw方法和dispatchDraw方法的差异这里也稍微解释一下,ViewGroup容器组件的绘制,当它没有背景时直接调用的是dispatchDraw方法, 而绕过了onDraw方法,当它有背景的时候就调用onDraw方法,而onDraw()方法里包含了dispatchDraw方法的调用。也就是说这里必须复写dispathDraw方法,不然将没效果。插件


定义好ViewGroup之后,接下来将它使用到页面的根布局上面,运行项目看结果:3d


咱们看到页面确实也成功灰度化了,心中暗暗窃喜,终于能够减小不少工做量了。然而问题仍是有的,项目中有几百个页面,每一个页面ViewGroup又不同,我难道要先准备GrayLinearLayout、GrayRelativeLayout、GrayFramelayout等等,而后一个一个去换根布局吗,想一想都有点累,那有没有更加优雅的方式呢?cdn

3、步步逼近,从源码中寻找最优雅的全局替换方式

通常来讲,最好用的方案每每来源于源码中,这也是资深程序员常常查看源码的缘由。带着这个疑问咱们先来简单复习一下View的加载过程,在我以前分享插件化的时候有简单提到过。咱们从setContentView方法中一路点下去,最终会看到调用了LayoutInflater类中的createViewFromTag方法,这段调用过程很简单,没看过的同窗能够亲自去查看一下。笔者点了不少次了,这里就直接贴出来相关代码吧,重点是红框里面的部分。xml


在红框里是具体的生成View过程,这里分为3种状况,优先Factory2,其次是Factory,最后是默认的onCreateView方法。关于这里为何会有3种状况,是由于历史缘由,为了兼容AppCompact。默认状况下,前两个都是为空的,会直接进入onCreateView方法。那么思路来了,咱们能够能够在加载View的时候,经过替换BaseActivity的布局统一替换全部页面呢?

方式一:经过setFactory方法给定咱们本身的Factory从而替代系统的加载View,而后实现统一替换。

在Activity的onCreate方法中调用如下代码,hook住系统加载View的流程,根据前面的源码,若是设置了factory将进入自定义的onCreateView方法而再也不进入系统的onCreateView方法。看过AMS或者事件分发流程的同窗确定知道,在咱们本身写的xml根布局之上还有一个系统的FrameLayout,关于这一块不太懂的同窗能够去看我以前的事件分发文章,里面有详细讲到。这里咱们就利用这一点,找到这个id是"content"的系统FrameLayout,而后将该FrameLayout替换成咱们本身的带有置灰代码的新FrameLayout便可。


方式二:走系统的流程,在系统的回调方法中替换

经过前面的源码得知,在不设置Factory的状况下,将调用到系统的onCreateView方法,因此咱们也能够直接在Activity的onCreateView方法中加上以上代码,效果是同样的。


这两种方式差异不大,都是能够用的,这里建议使用第二种方式,尽可能走系统自身的回调。


总结

本次咱们从app全局置灰怎么来实现这一话题,渐渐深刻,不断探索更优雅的实现方式。将一个ImageView置灰的猜测,引伸出了十几行代码就置换了项目中全部的View,本质也是经过查看源码的方式,一步一步地套出了方案。本文方案参考自鸿洋大神的技术文章,笔者在此基础上进行了一些拓展和源码解释,这里也必须感谢一下大神们的默默付出,站在巨人的肩膀上感受真心不错。看完之后对内容有疑问或者有改进建议的同窗,欢迎一块儿探讨学习,共同进步!

本文参考自原文:juejin.im/post/5e8893…

相关文章
相关标签/搜索