如何用 10 行代码让 app 全局置灰,扫地阿姨看完都学会了
一 、如何实现页面灰度化
实现灰度化的思路应该从 Paint 出发,因为系统是通过 Paint 将内容绘制到界面上的,如果能找到 Paint 相关的设置方法,那就再也合适不过了。自定义 View 做得多的同学可能知道 Paint 中可以设置 ColorMatrix,以下是其源码,从源码的注释我们可以看到如果将 setSaturation 的 sat 参数设置为 0 就代表灰度模式。
![](https://user-gold-cdn.xitu.io/2020/4/7/171534e8269e80ab?imageView2/0/w/1280/h/96
0/ignore-error/1)
OK,说干就干,我们自定义一个图片控件,修改其 Paint 中的 Matrix 属性试试。首先初始化 ColorMatrix 对象,然后按照源码中的说法,将 setSaturation 参数的值设置为 0,接下来将该 ColorMatrix 设置到 Paint 中,最后再 onDraw 方法中使用刚刚的 Paint 对象,代码大致如下:
以上是在 ImageView 中实现灰度化,那么文本的 TextView 其实也是一样的,因为本质都是使用 Paint 进行的绘制,所以可以直接将上面代码拷贝到自定义 TextView 中。
接下来我们在页面中使用上面自定义好的 2 个 View,我们运行一下,看一下效果。不出所以然,文本和图片都变成了灰色,具体运行结果如下图:
效果是实现了,又有一个新的问题摆在眼前。在项目里数以万计的 ImageView 和 TextView,总不可能一个一个来替换吧,这样做要换到猴年马月去?
二、如何提升替换效率
从上面 TextView、ImageView 二者的置灰实现没有任何区别,我们可以猜测是不是所有的 View 都能给置灰呢,那么 ViewGroup 作为一个特殊的 View 是否可以置灰呢?接下来我们来验证一下这个猜想,自定义一个 RelativeLayout,还是之前的代码,需要注意的是这里需要复写 dispatchDraw 方法。关于 onDraw 方法和 dispatchDraw 方法的差别这里也稍微解释一下,ViewGroup 容器组件的绘制,当它没有背景时直接调用的是 dispatchDraw 方法, 而绕过了 onDraw 方法,当它有背景的时候就调用 onDraw 方法,而 onDraw()方法里包含了 dispatchDraw 方法的调用。也就是说这里必须复写 dispathDraw 方法,否则将没效果。
定义好 ViewGroup 以后,接下来将它使用到页面的根布局上面,运行项目看结果:
我们看到页面确实也成功灰度化了,心中暗暗窃喜,终于可以减少很多工作量了。然而问题还是有的,项目中有几百个页面,每个页面 ViewGroup 又不一样,我难道要先准备 GrayLinearLayout、GrayRelativeLayout、GrayFramelayout 等等,然后一个一个去换根布局吗,想想都有点累,那有没有更加优雅的方式呢?
三、步步逼近,从源码中寻找最优雅的全局替换方式
一般来说,最好用的方案往往来源于源码中,这也是资深程序员经常查看源码的原因。带着这个疑问我们先来简单复习一下 View 的加载过程,在我之前分享插件化的时候有简单提到过。我们从 setContentView 方法中一路点下去,最终会看到调用了 LayoutInflater 类中的 createViewFromTag 方法,这段调用过程很简单,没看过的同学可以亲自去查看一下。笔者点了很多次了,这里就直接贴出来相关代码吧,重点是红框里面的部分。
评论