在 Android 12 中,提供了一些用于实现窗口模糊处理效果(例如背景模糊处理和模糊处理后方屏幕)的公共 API。窗口模糊处理或跨窗口模糊处理用于模糊处理给定窗口后方的屏幕。
有两种窗口模糊处理方式,可用于实现不同的视觉效果:
背景模糊处理(Background blur):可用于创建具有模糊背景的窗口,创造出磨砂玻璃效果,模糊区域是窗口。
模糊处理后方屏幕(Blur behind):可用于模糊处理(对话框)窗口后方的整个屏幕,创造出景深效果,模糊区域是整个屏幕。
这两种效果可以单独使用,也可以组合使用,如下图所示:
上面的三张效果图是谷歌官方所提供的效果图:
(a)仅背景模糊处理(Background blur)
(b)仅模糊处理后方屏幕(Blur behind)
(c)背景模糊处理和模糊处理后方屏幕(Background blur)+(Blur behind)
上一篇我们已经讲述了Android12如何使用原生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;}
上一篇:恋爱上的事,是最无道理可言的