1. App 页面置灰需求背景

当前新冠肺炎疫情正在全球蔓延,逝世人数众多。政府政府规定今年的 4 月 4 号为哀悼日,所有互联网项目能置灰的要跟随置灰处理。淘宝、京东等部分 App 都显示为灰色,对抗击新冠肺炎疫情斗争牺牲烈士和逝世同胞的深切哀悼。在 Android 开发中如何实现全局置灰是开发人员需要解决的技术问题。

2. 如何实现

将 App 页面置灰有多种实现方案,例如,最简单的实现方式是,在 App 内置 2 套切图及对应的 2 套色值资源文件等,根据全局置灰开关,选择加载不同的图片和色值。这种方案全局实现起来工作繁琐、工作量大。且会导致 App 包增大很多,用户体验差,不是一种很好的实现方案。

实现灰度化的思路应该从 Android 系统界面绘制原理出发寻找实现方案。系统是通过 Paint 将内容绘制到界面上的,Paint 中可以设置 ColorMatrix ,界面置灰可以通过通过使用颜色矩阵(ColorMatrix)来实现。

在 Android 中,系统使用一个颜色矩阵 ColorMatrix,来处理图像的色彩效果。通过这个类,可以很方便地改变矩阵值来处理颜色效果。ColorMatrix 提供了以下关键方法进行对颜色的处理:

  • setRotate(int axis, float degree) :设置颜色的色调,第一个参数,系统分别使用0、1、2 来代表 Red、Green、Blue 三种颜色的处理;而第二个参数,就是需要处理的值。
  • setSaturation(float sat):设置颜色的饱和度,参数代表设置颜色饱和度的值,当饱和度为0时,图像就变成灰度图像。
  • setScale(float rScale, float gScale, float bScale , float aScale):对于亮度进行处理。

实现去色重点用到了 ColorMatrix 类中的 setSaturation 方法,查看官网 API 对这个方法有这么一段说明:“Set the matrix to affect the saturation of colors. A value of 0 maps the color to gray-scale. 1 is identity. ” 意思是去设置颜色矩阵的饱和度,0 是灰色的,1 是原图。通过调用 setSaturation (0) 方法即可将图像置为灰色 。通过继承 ImageView 实现该方案验证,具体代码如下:

class GrayImageView extends ImageView {
    private Paint mPaint;
    public GrayImageView(Context context) {
        super(context,null);
    }
    public GrayImageView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        mPaint = new Paint();
        ColorMatrix colorMatrix = new ColorMatrix();
        colorMatrix.setSaturation(0);
       mPaint.setColorFilter( new ColorMatrixColorFilter(colorMatrix));
    }
    
    @Override
    protected void onDraw(Canvas canvas) {
        canvas.saveLayer(null,mPaint,Canvas.ALL_SAVE_FLAG);
        super.onDraw(canvas);
    }
}

测试效果如下:

       

以上是在 ImageView 中实现灰度化。针对其他控件例如 TextView 实现也是一样的,因为本质都是使用 Paint 进行的绘制,通过自定义 TextView 添加上面的代码也同样可以实现。通过自定义 View 可以实现颜色置灰,但是在项目里有很多的 ImageView、 TextView 等等各种控件,一个个替换工作量也特别的大。

3. 方案优化

从上面 ImageView 的置灰实现猜测是否所有的 View 都能给置灰,DecorView 是 Activity 窗口的根视图,根视图通过 ColorMatrix 设置置灰应该可以在全部子元素有效。在 BaseActivity 中添加代码如下:

@Override
protected void onCreate(Bundle savedInstanceState) {
    val paint = Paint()
    val cm = ColorMatrix()
    cm.setSaturation(0f)
    paint.colorFilter = ColorMatrixColorFilter(cm)
    window.decorView.setLayerType(View.LAYER_TYPE_HARDWARE, paint)
}

 测试所有的界面均变为灰色,效果如图:

     

这种方式使用很少的代码就实现了全局的置灰处理,明显提升了替换效率。

4. 总结

本文主要介绍了 Android App 中实现全局置灰的实现方式,旨在让大家对于 App 置灰实现有一个了解,提供一种解决思路。实现方式可能还有很多种,希望能够相互交流,互相借鉴。

 

每日一问的答案中可能无法全完解读这个问题,如果您是相关技术专家或者是对本问题有自己的见解,欢迎带着「批判性」的态度阅读,指正其中的不足。