Android12窗口模糊(二)高斯模糊API源码解析
创始人
2024-02-05 18:27:55
0

前言

在 Android 12 中,提供了一些用于实现窗口模糊处理效果(例如背景模糊处理和模糊处理后方屏幕)的公共 API。窗口模糊处理或跨窗口模糊处理用于模糊处理给定窗口后方的屏幕。
有两种窗口模糊处理方式,可用于实现不同的视觉效果:

  • 背景模糊处理(Background blur):可用于创建具有模糊背景的窗口,创造出磨砂玻璃效果,模糊区域是窗口。

  • 模糊处理后方屏幕(Blur behind):可用于模糊处理(对话框)窗口后方的整个屏幕,创造出景深效果,模糊区域是整个屏幕。

这两种效果可以单独使用,也可以组合使用,如下图所示:

上面的三张效果图是谷歌官方所提供的效果图:
在这里插入图片描述
(a)仅背景模糊处理(Background blur)
(b)仅模糊处理后方屏幕(Blur behind)
(c)背景模糊处理和模糊处理后方屏幕(Background blur)+(Blur behind)

上一篇我们已经讲述了Android12如何使用原生API实现高斯模糊效果,本篇文章我们将会结合源码具体分析原生API实现高斯模糊效果的底层逻辑。

一、API对应的源码

1、PhoneWindow的setBackgroundBlurRadius方法如下所示:

frameworks/base/core/java/com/android/internal/policy/PhoneWindow.java

public class PhoneWindow extends Window implements MenuBuilder.Callback {@Overridepublic final void setBackgroundBlurRadius(int blurRadius) {super.setBackgroundBlurRadius(blurRadius);if (CrossWindowBlurListeners.CROSS_WINDOW_BLUR_SUPPORTED) {if (mBackgroundBlurRadius != Math.max(blurRadius, 0)) {mBackgroundBlurRadius = Math.max(blurRadius, 0);mDecor.setBackgroundBlurRadius(mBackgroundBlurRadius);}}}
}

setBackgroundBlurRadius首先判断用户有没有设置blurRadius,如果设置了会继续调用DecorView的setBackgroundBlurRadius方法。

2、DecorView的setBackgroundBlurRadius方法如下所示:

frameworks/base/core/java/com/android/internal/policy/DecorView.java

public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {void setBackgroundBlurRadius(int blurRadius) {mOriginalBackgroundBlurRadius = blurRadius;if (blurRadius > 0) {if (mCrossWindowBlurEnabledListener == null) {mCrossWindowBlurEnabledListener = enabled -> {mCrossWindowBlurEnabled = enabled;updateBackgroundBlurRadius();};getContext().getSystemService(WindowManager.class).addCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener);} else {//更新高斯模糊背景的高斯半径updateBackgroundBlurRadius();}} else if (mCrossWindowBlurEnabledListener != null) {updateBackgroundBlurRadius();removeBackgroundBlurDrawable();}}}

DecorView的setBackgroundBlurRadius方法进行一些判断之后,会调用一个比较关键的方法updateBackgroundBlurRadius。

3、DecorView的updateBackgroundBlurRadius方法如下所示:

    //更新高斯模糊背景的高斯半径private void updateBackgroundBlurRadius() {//如果viewRootImpl为空直接返回if (getViewRootImpl() == null) return;//获取背景高斯模糊效果半径mBackgroundBlurRadius = mCrossWindowBlurEnabled && mWindow.isTranslucent()? mOriginalBackgroundBlurRadius : 0;if (mBackgroundBlurDrawable == null && mBackgroundBlurRadius > 0) {//调用ViewRootImpl的方法创建高斯模糊Drawable文件mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable();updateBackgroundDrawable();}if (mBackgroundBlurDrawable != null) {mBackgroundBlurDrawable.setBlurRadius(mBackgroundBlurRadius);}}

updateBackgroundBlurRadius方法首先获取ViewRootImpl实例对象,如果为空直接返回;如果不为空则获取背景高斯模糊效果半径,当mCrossWindowBlurEnabled 为true且窗口时透明样式的时候,才会获取之前设置的高斯模糊效果半径,否则高斯模糊效果半径直接设置为0;然后会检测mBackgroundBlurDrawable是否为空,如果为空且获取的mBackgroundBlurRadius大于0,便会调用ViewRootImpl的createBackgroundBlurDrawable方法创建BackgroundBlurDrawable对象实例。

4、ViewRootImpl的createBackgroundBlurDrawable方法如下所示:

frameworks/base/core/java/android/view/ViewRootImpl.java

public final class ViewRootImpl implements ViewParent,View.AttachInfo.Callbacks, ThreadedRenderer.DrawCallbacks,AttachedSurfaceControl {private final BackgroundBlurDrawable.Aggregator mBlurRegionAggregator =new BackgroundBlurDrawable.Aggregator(this);public BackgroundBlurDrawable createBackgroundBlurDrawable() {return mBlurRegionAggregator.createBackgroundBlurDrawable(mContext);}}

createBackgroundBlurDrawable方法仅仅是进一步调用mBlurRegionAggregator的createBackgroundBlurDrawable方法,mBlurRegionAggregator是BackgroundBlurDrawable的一个内部静态类。

5、Aggregator的createBackgroundBlurDrawable方法如下所示:

public final class BackgroundBlurDrawable extends Drawable {public static final class Aggregator {private final ViewRootImpl mViewRoot;...代码省略...public Aggregator(ViewRootImpl viewRoot) {mViewRoot = viewRoot;}/*** 使用默认背景模糊圆角半径创建一个模糊区域*/public BackgroundBlurDrawable createBackgroundBlurDrawable(Context context) {BackgroundBlurDrawable drawable = new BackgroundBlurDrawable(this);drawable.setBlurRadius(context.getResources().getDimensionPixelSize(R.dimen.default_background_blur_radius));return drawable;}...代码省略...}
}    

Aggregator的createBackgroundBlurDrawable将自己作为参数,创建了一个继承自Drawable的BackgroundBlurDrawable对象实例,并设置模糊半径为dimens.xml文件中配置的default_background_blur_radius字段。

6、重新回到第3步DecorView的updateBackgroundBlurRadius方法中:

    //更新高斯模糊背景的高斯半径private void updateBackgroundBlurRadius() {...代码省略...if (mBackgroundBlurDrawable == null && mBackgroundBlurRadius > 0) {mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable();//更新背景DrawableupdateBackgroundDrawable();}if (mBackgroundBlurDrawable != null) {mBackgroundBlurDrawable.setBlurRadius(mBackgroundBlurRadius);}}

在调用createBackgroundBlurDrawable方法为mBackgroundBlurDrawable进行赋值之后,会继续调用DecorView的updateBackgroundDrawable方法来更新当前DecorView所对应的背景Drawable,最后再使用mBackgroundBlurRadius更新mBackgroundBlurDrawable的模糊圆角半径数值。

7、DecorView的updateBackgroundDrawable方法如下所示:

    private void updateBackgroundDrawable() {// Background insets can be null if super constructor calls setBackgroundDrawable.if (mBackgroundInsets == null) {mBackgroundInsets = Insets.NONE;}if (mBackgroundInsets.equals(mLastBackgroundInsets)&& mBackgroundBlurDrawable == mLastBackgroundBlurDrawable&& mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) {return;}Drawable destDrawable = mOriginalBackgroundDrawable;if (mBackgroundBlurDrawable != null) {//将新获取的类型为BackgroundBlurDrawable的mBackgroundBlurDrawable// 和原来类型为Drawable的mOriginalBackgroundDrawable合并成一个LayerDrawable实例对象destDrawable = new LayerDrawable(new Drawable[]{mBackgroundBlurDrawable,mOriginalBackgroundDrawable});}if (destDrawable != null && !mBackgroundInsets.equals(Insets.NONE)) {//将当前类型为LayerDrawable的destDrawable再封装成InsetDrawable实例对象destDrawable = new InsetDrawable(destDrawable,mBackgroundInsets.left, mBackgroundInsets.top,mBackgroundInsets.right, mBackgroundInsets.bottom) {/*** Return inner padding so we don't apply the padding again in* {@link DecorView#drawableChanged()}*/@Overridepublic boolean getPadding(Rect padding) {return getDrawable().getPadding(padding);}};}//调用父类方法设置这个类的背景Drawablesuper.setBackgroundDrawable(destDrawable);mLastBackgroundInsets = mBackgroundInsets;mLastBackgroundBlurDrawable = mBackgroundBlurDrawable;mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable;}

相关内容

热门资讯

三国人物的歇后语 三国人物有关的歇后语(精选80条)  歇后语是中国劳动人民自古以来在生活实践中创造的一种特殊语言形式...
献给母亲的诗 献给母亲的一首诗  在学习、工作乃至生活中,大家一定都接触过一些使用较为普遍的诗歌吧,诗歌是一种抒情...
黑夜里天空是愤怒的拳头作文 黑夜里天空是愤怒的拳头作文  在学习、工作或生活中,大家都不可避免地要接触到作文吧,作文是一种言语活...
他的父亲作文 他的父亲作文(5篇)  无论是身处学校还是步入社会,大家都有写作文的经历,对作文很是熟悉吧,作文是通...
向阳花木易逢春的作文 向阳花木易逢春的作文(通用26篇)  在学习、工作乃至生活中,大家总少不了接触作文吧,作文一定要做到...
以五一为话题的作文 以五一为话题的作文(精选26篇)  在学习、工作或生活中,大家都经常看到作文的身影吧,作文是经过人的...
责任与担当作文 责任与担当作文800字(精选25篇)  在学习、工作或生活中,大家都接触过作文吧,借助作文人们可以反...
离职申请书的离职原因 离职申请书的离职原因5篇  在经济飞速发展、人们往来越来越密切的今天,申请书使用的情况越来越多,申请...
月光下的村庄铁蛋作文 月光下的村庄铁蛋作文(精选25篇)  在日常的学习、工作、生活中,大家都尝试过写作文吧,作文一定要做...
优秀作文音乐人生 优秀作文音乐人生  音乐,是一种快乐;音乐,是一种享受;音乐,是我们童年的好朋友;音乐,也是我的人生...
动漫《镇魂街》插曲歌词 动漫《镇魂街》插曲歌词  闪耀 - 南征北战  (动漫《镇魂街》插曲)  作词:南征北战  作曲:南...
美好的旅行作文 美好的旅行作文  在日复一日的学习、工作或生活中,大家都不可避免地会接触到作文吧,作文是从内部言语向...
「励志故事」历数那些80后C... 「励志故事」历数那些80后CEO的校园生活  【马克·扎克伯格的校园生活】  年龄:26岁  净资产...
中考以奖品为话题作文600字 中考以奖品为话题作文600字(通用17篇)  在日常生活或是工作学习中,大家都跟作文打过交道吧,写作...
留一点微笑给自己作文 留一点微笑给自己作文600字(通用28篇)  在日常生活或是工作学习中,大家都跟作文打过交道吧,写作...
军训必唱歌曲《弹起我心爱的土... 军训必唱歌曲《弹起我心爱的土琵琶》歌词   《弹起我心爱的土琵琶》  西边的太阳快要落山了  微山湖...
记忆深处奶奶给我的美味作文 记忆深处奶奶给我的美味作文  在平凡的学习、工作、生活中,大家都不可避免地会接触到作文吧,作文是经过...
积极思考的作文 关于积极思考的作文  导语:今天小编整理了两篇关于积极思考的作文,欢迎大家的围观!篇1:积极思考  ...
梦想,从这里起航作文 梦想,从这里起航作文  在日常的学习、工作、生活中,大家都接触过作文吧,作文是人们把记忆中所存储的有...
刮风的夜晚作文600字 刮风的夜晚作文600字  无论是身处学校还是步入社会,许多人都有过写作文的经历,对作文都不陌生吧,借...