Android App 的“黑白化”,Android外包是如何转正网易的

cm.setSaturation(0);

mPaint.setColorFilter(new ColorMatrixColorFilter(cm));

}

@Override

public void draw(Canvas canvas) {

canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);

super.draw(canvas);

canvas.restore();

}

@Override

protected void dispatchDraw(Canvas canvas) {

canvas.saveLayer(null, mPaint, Canvas.ALL_SAVE_FLAG);

super.dispatchDraw(canvas);

canvas.restore();

}

}

代码很简单,但是注意有个细节,注意我们也复写了 dispatchDraw,为什么呢?自己思考。

我们更换下 xml:
<?xml version="1.0" encoding="utf-8"?>

<com.imooc.imooc_wechat_app.view.GrayLinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:orientation="vertical"

tools:context=".TestActivity">

<ImageView

android:layout_width="100dp"

android:layout_height="wrap_content"

android:src="@mipmap/logo" />

<TextView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="鸿洋真帅"

android:textColor="@android:color/holo_red_light"

android:textSize="30dp" />

<Button

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="鸿洋真帅"

android:textColor="@android:color/holo_red_light"

android:textSize="30dp" />

</com.imooc.imooc_wechat_app.view.GrayLinearLayout>

我们放了蓝色 Logo 的 ImageView,红色字体的 TextView 和 Button,看一眼效果:

完美!

是不是又有点茅塞顿开!

只要我们换了 我们设置的Activity 的根布局就可以了!

Activity 的根布局可能是 LinearLayout,FrameLayout,RelativeLayout,ConstraintLayout...

换个鸡儿...这得换到啥时候,跟刚才有啥区别。

还有思路吗,没什么确定的 View 吗?

再想想。

我们的设置的 Activity 的根布局会放在哪?

android.id.content

是不是这个 Content View 上?

这个 content view 目前一直是 FrameLayout !

那么我们只要在生成这个android.id.content 对应的 FrameLayout,换成 GrayFrameLayout 就可以了。

怎么换呢?

appcompat 那一套?去搞 LayoutFactory?

确实可以哈,但是那样要设置 LayoutFactory,还需要考虑 appcompat 相关逻辑。

有没有那种不需要去修改什么流程的方案?

<>LayoutInflater 中的细节


还真是有的。

我们的 AppCompatActivity,可以复写 onCreateView 的方法,这个方法其实也是LayoutFactory在构建 View 的时候回调出来的,一般对应其内部的mPrivateFactory。

他的优先级低于 Factory、Factory2,相关代码:

if (mFactory2 != null) {

view = mFactory2.onCreateView(parent, name, context, attrs);

} else if (mFactory != null) {

view = mFactory.onCreateView(name, context, attrs);

} else {

view = null;

}

if (view == null && mPrivateFactory != null) {

view = mPrivateFactory.onCreateView(parent, name, context, attrs);

}

if (view == null) {

final Object lastContext = mConstructorArgs0;

mConstructorArgs0 = context;

try {

if (-1 == name.indexOf('.')) {

view = onCreateView(parent, name, attrs);

} else {

view = createView(name, null, attrs);

}

} finally {

mConstructorArgs0 = lastContext;

}

}

但是目前对于 FrameLayout,appcompat 并没有特殊处理,也就是说你可以在 onCreateView 回调中去构造 FrameLayout 对象。

很简单,就复写 Activity 的 onCreateView 方法即可:

public class TestActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_test);

}

@Override

public View onCreateView(String name, Context context, AttributeSet attrs) {

return super.onCreateView(name, context, attrs);

}

}

我们在这个方法中把content view 对应的 FrameLayout 换成 GrayFrameLayout.

@Override

public View onCreateView(String name, Context context, AttributeSet attrs) {

if("FrameLayout".equals(name)){

int count = attrs.getAttributeCount();

for (int i = 0; i < count; i++) {

String attributeName = attrs.getAttributeName(i);

String attributeValue = attrs.getAttributeValue(i);

if (attributeName.equals("id")) {

int id = Integer.parseInt(attributeValue.substring(1));

String idVal = getResources().getResourceName(id);

if ("android:id/content".equals(idVal)) {

GrayFrameLayout grayFrameLayout = new GrayFrameLayout(context, attrs);

return grayFrameLayout;

}

}

}

}

return super.onCreateView(name, context, attrs);

}

代码应该都能看明白吧,我们找到 id 是 android:id/content 的,换成了我们的 GrayFrameLayout。

最后看一眼GrayFrameLayout:

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();

}

}

好了,运行一下,看下效果:

效果 ok。

然后把onCreateView 这坨代码,放到你的 BaseActivity里面就行了。

什么,没有 BaseActivity?

...自己玩去吧。

<>找个 App验证下


说到现在,都没有脱离出一个 Activity。

我们找个复杂点的项目验证下好吧。

我去 github 找个 wanandroid 的 Java 开源项目:

选中了:https://github.com/jenly1314/WanAndroid

导入后,只要在 BaseActivity 里面添加我们刚才的代码就可以了。

运行效果图:

恩,没错,webview 里面的文字,图片都黑白化了。

这样一个 app 就完全黑白化了。

等等,我发现状态栏没变,状态栏是不是有 API,自己在 BaseActivity 里面调用一行代码处理哈。

点击此处回复:「文章写的真好」,可以获取黑白化后的 apk,自己体验。

<>真的没问题了吗?


其实没运行出来问题有些遗憾。

那我自爆几个问题吧。

1. 如果 Activity的 Window 设置了 background,咋办呢?

因为我们处理的是 content view,肯定在 window 之下,肯定覆盖不到 window 的 backgroud。

咋办咋办?

不要慌。

我们生成的GrayFrameLayout也是可以设置 background 的?

if ("android:id/content".equals(idVal)) {

GrayFrameLayout grayFrameLayout = new GrayFrameLayout(context, attrs);

grayFrameLayout.setBackgroundDrawable(getWindow().getDecorView().getBackground());

return grayFrameLayout;

}

如果你是theme 中设置的 windowBackground,那么需要从 theme 里面提取 drawable,参考代码如下:

TypedValue a = new TypedValue();

getTheme().resolveAttribute(android.R.attr.windowBackground, a, true);

if (a.type >= TypedValue.TYPE_FIRST_COLOR_INT && a.type <= TypedValue.TYPE_LAST_COLOR_INT) {

// windowBackground is a color

int color = a.data;

} else {

// windowBackground is not a color, probably a drawable

Drawable c = getResources().getDrawable(a.resourceId);

}

2.Dialog 支持吗?

这个方案默认就已经支持了 Dialog 黑白化,为什么?自己撸一下 Dialog 相关源码,看看 Dialog 内部的 View 结构是什么样子的。

另外 webview 内部的图片文字也支持。

3. 如果未来 android.R.id.content 不是 FrameLayout 咋办?

确实有这个可能。

想必你也有办法把PhoneWindow 的内部 View 搞成这个样子:

decorView

GrayFrameLayout

android.R.id.content

activity rootView

或者这个样子:

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级安卓工程师,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Android移动开发全套学习资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。



由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)

总结

本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!

在这里我也分享一份自己收录整理的**Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料**帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

资料》送给大家,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。**

外链图片转存中...(img-O5PeNaha-1710496149864)

外链图片转存中...(img-F5NQqiv5-1710496149864)

外链图片转存中...(img-4cSPa6GF-1710496149865)

外链图片转存中...(img-UY0Gc7tx-1710496149865)

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频
如果你觉得这些内容对你有帮助,可以添加下面V无偿领取!(备注Android)

外链图片转存中...(img-Gwbjru0h-1710496149866)

总结

本文讲解了我对Android开发现状的一些看法,也许有些人会觉得我的观点不对,但我认为没有绝对的对与错,一切交给时间去证明吧!愿与各位坚守的同胞们互相学习,共同进步!

在这里我也分享一份自己收录整理的**Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料**帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

相关推荐
爱笑鱼1 小时前
Binder(八):远端进程死了,BinderProxy 为什么还能收到通知?
android
Android-Flutter1 小时前
Kotlin 冷流与热流详解
android·kotlin
zhangphil2 小时前
Android ContentProvider/ContentResolver读图片,跨进程 IPC慢
android
yueqc12 小时前
Android .so 文件压缩
android·so·包体积
龚礼鹏2 小时前
深入解析 Android Automotive (AAOS) 启动流程与 CarService 核心机制(基于 Android 16 最新源码视角)
android
rosmis2 小时前
agent各指标定义
android·java·开发语言
风样滴男人哟4 小时前
PHP特性之反射类ReflectionClass机制
android·开发语言·php
小孔龙5 小时前
RenderNode 与 DisplayList:Android 硬件加速的绘制记录与复用
android·性能优化
qizayaoshuap5 小时前
鸿蒙 ArkTS 实战:搜索列表 城市实时过滤(示例 94)
android·华为·harmonyos
朝星5 小时前
Framework筑基之Handler消息机制5(应用层)
android