在清明节时各大APP都会进行黑白化处理,当时在接到这个需求的时候感觉好麻烦,是不是又要搞一套皮肤?
然而在一系列搜索之后,找到了两位大神(鸿洋、U2tzJTNE)的实现方案,其实相当的简单!
让我们一起站在巨人的肩膀上来分析一下原理,并思考会不会有更简便的实现?
两位大神的置灰方案是相同的,都能看到一段同样的代码:
他们都用了 Android提供的ColorMatrix(颜色矩阵),将其饱和度设置为0,这样使用Paint绘制出来的都是没有饱和度的灰白样式!
然而两位在何时使用Paint绘制时选择了不同方案。
鸿洋老师分析,如果我们 把每个Activity的根布局饱和度设置为0 是不是就可以了?
那根布局是谁?
鸿洋老师分析我们的布局最后setContentView最后都会设置到一个R.id.content的FrameLayout当中。
我们去自定义一个GrayFrameLayout,在draw的时候使用这个饱和度为0的画笔,被这个FrameLayout包裹的布局都会变成黑白。
然后我们用GrayFrameLayout去替换这个R.id.content的FrameLayout ,是不是就可以做到将页面黑白化了?
替换FrameLayout的方法可以去【鸿洋】这篇文章下查看。
U2tzJTNE大佬使用了另一种巧妙的方案。
他先创建了一个具有数据变化感知能力的ObservableArrayList (当内容发生变化有回调)。
之后使用反射将WindowManagerGlobal内的mViews容器(ArrayList,该容器会存放所有的DecorView),替换为ObservableArrayList ,这样就可以监听到每个DecorView的创建,并且拿到View本身。
拿到DecorView,那就可以为所欲为了!
大佬使用了 setLayerType(View.LAYER_TYPE_HARDWARE, mPaint) ,对布局进行了重绘。至于为什么要用LAYER_TYPE_HARDWARE?因为默认的View.LAYER_TYPE_NONE会把Paint强制设置为null。
只需要在Application里面调用该方法即可。
两位大佬的方案都非常的棒,咱们理性的来对比一下。
既然研究明白了大佬的方案,那有没有又不需要反射,设置又简单的方法呢?
能不能使用原生方式获取DecorView的实例呢?
突然灵光一闪, Application里面不是有registerActivityLifecycleCallbacks这个注册监听方法吗?监听里面的onActivityCreated不是可以获取到当前的Activity吗?那DecorView不就拿到了!
搞起!上代码!
这样看起来是不是更简单了!使用了APP原生的方法实现了黑白化!当然也有缺点,因为在Activity级别设置,无法做到在当前页面即时变为黑白。
这三种方案因为都使用了颜色矩阵,所以坑都是一样的,请注意。
在我们可以设置渲染的时候windowBackground已经展示完毕了。
解决方案:只能在当前的包里修改,或者不去理会。
因为我们使用了setLayerType进行重绘,而SurfaceView是有独立的Window,脱离布局内的Window,运行在其他线程,不影响主线程的绘制,所以当前方案无法使SurfaceView变色。
解决方案:
1、使用TextureView。
2、看下这个SurfaceView是否可以设置滤镜,正常都是一些三方或者自制的播放器。
我们可能会在APP内置小程序,小程序基本是运行在单独的进程中,但是如果我们的黑白配置在运行过程中发生变化,其他进程是无法感知的。
解决方案:使用MMKV存储黑白配置,并设置多进程共享,在开启小程序之前都判断一下黑白展示。
最后咱们再总结一下黑白化方案。
使用了ColorMatrix设置饱和度为0,设置到Paint中,让根布局拿着这个Paint去进行重绘。
这样APP全局黑白化的介绍就结束了,希望大家读完这篇文章,会对APP黑白化有一个更深入的了解。