如何实现特殊日期界面灰色模式?
在特殊的日期为了缅怀和纪念,比如清明节等,淘宝、京东、拼多多等应用的首页自动切换成灰白主题的样式。
先看看如何快速实现这个功能,服务端可以将图片灰度预处理,前端也能将界面呈现灰度。按照置灰作用区域分:局部View、单个Activity页面、整个应用。
①局部View置灰:淘宝APP使用局部View灰色的方法,首页往下滑动出来的不再呈现灰白色。
②单个Activity置灰:优酷、知乎等APP使用单个Activity页面呈现灰色的方法。
③整个应用置灰:工商银行APP使用这种全局灰色的方法。
首先,要了解View中的setLayerType(int layerType,Paint paint)方法,借助该方法,创建一个新的透明图层,在新的图层上绘制,有类似“遮罩”的效果。
public void setLayerType(@LayerType int layerType, @Nullable Paint paint) {
if (layerType < LAYER_TYPE_NONE || layerType > LAYER_TYPE_HARDWARE) {
throw new IllegalArgumentException("Layer type can only be one of: LAYER_TYPE_NONE, "
+ "LAYER_TYPE_SOFTWARE or LAYER_TYPE_HARDWARE");
}
boolean typeChanged = mRenderNode.setLayerType(layerType);
if (!typeChanged) {
setLayerPaint(paint);
return;
}
if (layerType != LAYER_TYPE_SOFTWARE) {
// Destroy any previous software drawing cache if present
// NOTE: even if previous layer type is HW, we do this to ensure we've cleaned up
// drawing cache created in View#draw when drawing to a SW canvas.
destroyDrawingCache();
}
mLayerType = layerType;
mLayerPaint = mLayerType == LAYER_TYPE_NONE ? null : paint;
mRenderNode.setLayerPaint(mLayerPaint);
// draw() behaves differently if we are on a layer, so we need to
// invalidate() here
invalidateParentCaches();
invalidate(true);
}
其次,需要用到ColorMatrix颜色矩阵,常用于颜色的转换、图片处理。该类中有一个setSaturation(float sat)方法。当参数为 0 时转换为灰色,为 1 时恢复为原本的颜色。
/**
* Set the matrix to affect the saturation of colors.
*
* @param sat A value of 0 maps the color to gray-scale. 1 is identity.
*/
public void setSaturation(float sat) {
reset();
float[] m = mArray;
final float invSat = 1 - sat;
final float R = 0.213f * invSat;
final float G = 0.715f * invSat;
final float B = 0.072f * invSat;
m[0] = R + sat; m[1] = G; m[2] = B;
m[5] = R; m[6] = G + sat; m[7] = B;
m[10] = R; m[11] = G; m[12] = B + sat;
}
1、局部View置灰
借助View和ColorMatrix,可以很方便的使View呈现灰白效果。使用Kotlin,写成View的扩展方法,以方便调用。
fun View.grayMode() {
val colorMatrix = ColorMatrix().apply {
setSaturation(0f)
}
val paint = Paint().apply {
colorFilter = ColorMatrixColorFilter(colorMatrix)
}
setLayerType(View.LAYER_TYPE_HARDWARE, paint)
}
2、单个Activity置灰
除了让特定View/ViewGroup范围内的区域呈现灰白效果,还可以对Activity进行操作,让整个Activity页面变成灰白色。只需要利用Window中的DecorView,参考《Activity的组成结构》一文。
扩展方法如下:
fun Activity.grayMode() {
val colorMatrix = ColorMatrix().apply {
setSaturation(0f)
}
val paint = Paint().apply {
colorFilter = ColorMatrixColorFilter(colorMatrix)
}
window?.decorView?.setLayerType(View.LAYER_TYPE_HARDWARE, paint)
}
3、整个应用置灰
最后,还有全局页面的Hook。只需在Application里通过registerActivityLifecycleCallbacks()方法注册ActivityLifecycleCallbacks监听Activity的生命周期,参考《Activity生命周期监听ActivityLifecycleCallbacks》一文。使整个应用所有的Activity页面都能实现灰白效果:
override fun onCreate() {
super.onCreate()
//注册ActivityLifecycleCallbacks
registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks{
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
activity.grayMode()
}
...
})
}
市面上的应用基本上都只是首页特殊处理,其它页面还是正常显示,降低对使用体验的影响。
最后结合相关业务,在启动时通过服务端去更新客户端的配置,再根据此条件(比如日期)触发即可。
//Whether it is a mourning day or not, should a gray interface be displayed
if (!mourningDay()) return
...
不知道有没有人能注意到,“灰白模式”其实和开发者选项中的模拟颜色空间 > 全色盲调试选项效果类似。