0871-64605728
您当前位置:网站首页 >> 知识专区
安卓APP全局黑白化实现方案
文章来源:CSDN 老 匡  上传时间:2022-12-30  浏览量:400

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。


一、原理

两位大神的置灰方案是相同的,都能看到一段同样的代码:

Paint mPaint = new Paint(); ColorMatrix mColorMatrix = new ColorMatrix(); // 设置饱和度为0 mColorMatrix.setSaturation(0); mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix)); 
			

			
  • 1
  • 2
  • 3
  • 4
  • 5

他们都用了Android提供的ColorMatrix(颜色矩阵),将其饱和度设置为0,这样使用Paint绘制出来的都是没有饱和度的灰白样式!

然而两位在何时使用Paint绘制时选择了不同方案。

1.1 鸿洋:重写draw方法

鸿洋老师分析,如果我们把每个Activity的根布局饱和度设置为0是不是就可以了?

那根布局是谁?

鸿洋老师分析我们的布局最后setContentView最后都会设置到一个R.id.content的FrameLayout当中。

我们去自定义一个GrayFrameLayout,在draw的时候使用这个饱和度为0的画笔,被这个FrameLayout包裹的布局都会变成黑白。

// 转载自鸿洋 // https://blog.csdn.net/lmj623565791/article/details/105319752 public class GrayFrameLayout extends FrameLayout { private Paint mPaint = new Paint(); public GrayFrameLayout(Context context, AttributeSet attrs) { super(context, attrs); ColorMatrix cm = new ColorMatrix(); cm.setSaturation(0); mPaint.setColorFilter(new ColorMatrixColorFilter(cm)); } @Override protected void dispatchDraw(Canvas canvas) { canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG); super.dispatchDraw(canvas); canvas.restore(); } @Override public void draw(Canvas canvas) { canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG); super.draw(canvas); canvas.restore(); } } 
			

			
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

然后我们用GrayFrameLayout去替换这个R.id.content的FrameLayout,是不是就可以做到将页面黑白化了?

替换FrameLayout的方法可以去【鸿洋】这篇文章下查看。

1.2 U2tzJTNE:监听DecorView的添加

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。

// 转载自U2tzJTNE // https://juejin.cn/post/6892277675012915207 public static void enable(boolean enable) { try { //灰色调Paint final Paint mPaint = new Paint(); ColorMatrix mColorMatrix = new ColorMatrix(); mColorMatrix.setSaturation(enable ? 0 : 1); mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix)); //反射获取windowManagerGlobal @SuppressLint("PrivateApi") Class<?> windowManagerGlobal = Class.forName("android.view.WindowManagerGlobal"); @SuppressLint("DiscouragedPrivateApi") java.lang.reflect.Method getInstanceMethod = windowManagerGlobal.getDeclaredMethod("getInstance"); getInstanceMethod.setAccessible(true); Object windowManagerGlobalInstance = getInstanceMethod.invoke(windowManagerGlobal); //反射获取mViews Field mViewsField = windowManagerGlobal.getDeclaredField("mViews"); mViewsField.setAccessible(true); Object mViewsObject = mViewsField.get(windowManagerGlobalInstance); //创建具有数据感知能力的ObservableArrayList ObservableArrayList<View> observerArrayList = new ObservableArrayList<>(); observerArrayList.addOnListChangedListener(new ObservableArrayList.OnListChangeListener() { @Override public void onChange(ArrayList list, int index, int count) { } @Override public void onAdd(ArrayList list, int start, int count) { // 拿到DecorView触发重绘 View view = (View) list.get(start); if (view != null) { view.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); } } @Override public void onRemove(ArrayList list, int start, int count) { } }); //将原有的数据添加到新创建的list observerArrayList.addAll((ArrayList<View>) mViewsObject); //替换掉原有的mViews mViewsField.set(windowManagerGlobalInstance, observerArrayList); } catch (Exception e) { e.printStackTrace(); } } 
			

			
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51

只需要在Application里面调用该方法即可。

1.3 方案分析

两位大佬的方案都非常的棒,咱们理性的来对比一下。

  • 鸿洋老师: 使用自定义FrameLayout的方案需要一个BaseActivity统一设置,稍显麻烦,代码侵入性较强。

  • U2tzJTNE大佬: 方案更加简单、动态,一行代码设置甚至可以做到在当前页从彩色变黑白,但是使用了反射,有一点点性能消耗。

二、简易方案(直接复制)

既然研究明白了大佬的方案,那有没有又不需要反射,设置又简单的方法呢?

能不能使用原生方式获取DecorView的实例呢?

突然灵光一闪,Application里面不是有registerActivityLifecycleCallbacks这个注册监听方法吗?监听里面的onActivityCreated不是可以获取到当前的Activity吗?那DecorView不就拿到了!

搞起!上代码!

public class StudyApp extends Application { @Override public void onCreate() { super.onCreate(); Paint mPaint = new Paint(); ColorMatrix mColorMatrix = new ColorMatrix(); mColorMatrix.setSaturation(0); mPaint.setColorFilter(new ColorMatrixColorFilter(mColorMatrix)); registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() { @Override public void onActivityCreated(@NonNull Activity activity, @Nullable Bundle savedInstanceState) { // 当Activity创建,我们拿到DecorView,使用Paint进行重绘 View decorView = activity.getWindow().getDecorView(); decorView.setLayerType(View.LAYER_TYPE_HARDWARE, mPaint); } .... }); } } 
			

			
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

这样看起来是不是更简单了!使用了APP原生的方法实现了黑白化!当然也有缺点,因为在Activity级别设置,无法做到在当前页面即时变为黑白。

三、注意事项

这三种方案因为都使用了颜色矩阵,所以坑都是一样的,请注意。

3.1 启动图windowBackground无法变色

在我们可以设置渲染的时候windowBackground已经展示完毕了。

解决方案:只能在当前的包里修改,或者不去理会。

3.2 SurfaceView无法变色

因为我们使用了setLayerType进行重绘,而SurfaceView是有独立的Window,脱离布局内的Window,运行在其他线程,不影响主线程的绘制,所以当前方案无法使SurfaceView变色。

解决方案:
1、使用TextureView。
2、看下这个SurfaceView是否可以设置滤镜,正常都是一些三方或者自制的播放器。

3.3 多进程变色

我们可能会在APP内置小程序,小程序基本是运行在单独的进程中,但是如果我们的黑白配置在运行过程中发生变化,其他进程是无法感知的。

解决方案:使用MMKV存储黑白配置,并设置多进程共享,在开启小程序之前都判断一下黑白展示。

总结

最后咱们再总结一下黑白化方案。

使用了ColorMatrix设置饱和度为0,设置到Paint中,让根布局拿着这个Paint去进行重绘。

这样APP全局黑白化的介绍就结束了,希望大家读完这篇文章,会对APP黑白化有一个更深入的了解。如果我的文章能给大家带来一点点的福利,那在下就足够开心了。

07

2022-02

2022北京冬奥会开幕式里的黑科技,闪耀闪耀全世界

2022北京冬奥会开幕式里的黑科技,闪耀闪耀全世界

26

2021-04

Python基础语法和数据类型最全总结

总结了Python最全基础语法和数据类型总结,一文带你学会Python。

29

2021-06

计算机网络的 89 个核心概念

计算机网络的 89 个核心概念

18

2022-02

Redis6-雪崩、击穿、穿透、分布式锁

Redis6-雪崩、击穿、穿透、分布式锁

09

2021-07

那些不为人知的搜索引擎语法

你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法

21

2022-10

作业帮董晓聪:作业帮云原生降本增效实践之路

作业帮董晓聪:作业帮云原生降本增效实践之路

07

2022-02

公网IP、私网IP、动态IP、静态IP

公网IP、私网IP、动态IP、静态IP

10

2022-06

DeepMind爆发史:决定AI高峰的“游戏玩家”|深度学习崛起十年

DeepMind爆发史:决定AI高峰的“游戏玩家”|深度学习崛起十年
返回顶部
客服电话
0871-64605728
用微信扫一扫关注我们
请各公司推销人员注意:我单位拒绝任何方式、任何形式的电话推销,请勿拔打我单位客服热线进行电话推销,谢谢合作!
公司名称:云南昂略科技有限公司
联系地址:云南省昆明市官渡区永平路188号鑫都韵城写字楼6栋1004号
联系电话:0871-64605728、传真号码:0871-64605728
电子邮箱:19701580@qq.com
关键词:知识专区:安卓APP全局黑白化实现方案,云南昂略科技有限公司,云南移动执法平台建设,云南智慧安防调度系统,云南头戴式安全终端,昂略科技
云南网站建设,云南网页设计,昆明网站建设,昆明网页设计  网站管理
【版权声明】本站部分内容由互联网用户自行发布,著作权或版权归原作者所有。如果侵犯到您的权益请发邮件致info@ynjwz.com,我们会第一时间进行删除并表示歉意。