窗口模糊

在 Android 12 中,公共 API 可用于实现窗口模糊效果(例如背景模糊和背后模糊)。请注意,尽管您可能会在代码、开发人员文档或 UI 表示法中看到窗口模糊也称为交叉窗口模糊,但交叉窗口模糊与窗口模糊相同。

使用这些 API,您可以模糊自己窗口后面的任何内容。您可以创建背景模糊的窗口,创建磨砂玻璃效果,或显示窗口,使其后面的整个屏幕模糊,创建景深效果。您也可以将这两种效果结合起来。

仅背景模糊

1

只在后面模糊

2

背后和背景模糊

3

图 1. Background blur only (1), blur only behind (2), background blur and blur behind (3)

窗口模糊功能适用于整个窗口,这意味着当您正在查看的窗口后面有另一个应用程序时,它也可以工作。这与模糊渲染效果不同,后者会模糊同一应用程序中窗口内的内容。窗口模糊对于对话框和底部工作表以及其他浮动窗口很有用。

请务必注意,此功能使用大量 GPU 资源。因此,尽管它适用于所有 Android 设备,但仅支持那些具有足够 GPU 能力的设备。

执行

原始设备制造商和合作伙伴

默认情况下禁用窗口模糊。要在设备上启用模糊功能,请执行以下操作:

  • 确保设备可以处理额外的 GPU 负载 - 模糊操作很昂贵,并且在低端设备上,它可能会导致丢帧。仅在具有足够 GPU 能力的设备上启用此功能。
  • 确保您的librenderengine实现了模糊逻辑 - 默认的 Android 12 渲染引擎会这样做,但任何自定义渲染引擎都必须自己实现模糊逻辑。
  • 通过设置以下surface flinger sysprop来启用模糊:
# enable surface flinger window blurs
PRODUCT_PROPERTY_OVERRIDES += \
       ro.surface_flinger.supports_background_blur=1

第三方开发商

请参阅示例和源代码部分以查看示例代码。系统服务器可以在运行时禁用窗口模糊。因此,应用程序必须提供一个后备的、无模糊的版本。否则,如果模糊因为被禁用而未渲染,则窗口背景可能非常透明,以至于窗口内的内容变得难以辨认。如果您的应用不提供后备应用版本,请确保您的 UI 在启用模糊和禁用模糊的情况下都能正常工作。以下是可以随时禁用模糊的三种情况:

  1. 该设备运行的是 Android 11 或更低版本。由于窗口模糊仅在 Android 12 及更高版本的设备上可用,因此应用必须为运行 Android 11 及更低版本的设备实现备用的模糊体验替代方案。
  2. 该设备不支持窗口模糊,因为它们很昂贵,因此低端设备在渲染它们时可能会丢帧。对于这种情况,应用程序必须提供回退的模糊体验。
  3. 系统服务器(例如,在省电模式期间,或由于开发人员设置或隧道模式)在运行时禁用模糊。

上面的第2点和第 3点都由使用WindowManager.addCrossWindowBlurEnabledListener注册的侦听器报告。如果您的应用程序使用模糊 API,请注册此侦听器并在调用该侦听器时更新您的 UI,如果您想为启用模糊和禁用模糊状态使用不同的 UI。注册后,立即调用侦听器以报告当前是否启用了模糊。

使用以下方法实现模糊功能:

示例和来源

public class BlurActivity extends Activity {
   private final int mBackgroundBlurRadius = 150;
   private final Drawable mBackgroundDrawableWithBlur;
   private final Drawable mBackgroundDrawableNoBlur;

   private final int mBlurBehindRadius = 50;
   private final float mDimAmountWithBlur = 0.1f;
   private final float mDimAmountNoBlur = 0.6f;


   private Consumer<Boolean> mCrossWindowBlurEnabledListener = enabled -> {
       getWindow().setBackgroundDrawable(
               enabled ? mBackgroundDrawableWithBlur : mBackgroundDrawableNoBlur);
       getWindow().setDimAmount(enabled ? mDimAmountWithBlur : mDimAmountNoBlur);
   };

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.blur_activity);

       mBackgroundDrawableWithBlur = getContext().getResources().getDrawable(
               R.drawable.window_background_with_blur);
       mBackgroundDrawableNoBlur = getContext().getResources().getDrawable(
               R.drawable.window_background_no_blur);

       if (Android version >= Android S) {
           getWindow().addFlags(
                   WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
           window.getAttributes().setBlurBehindRadius(mBlurBehindRadius);
           window.setBackgroundBlurRadius(mBackgroundBlurRadius);
           getWindow().getDecorView().addOnAttachStateChangeListener(
                                         new View.OnAttachStateChangeListener() {
                    @Override
                    public void onViewAttachedToWindow(View v) {
                        getWindowManager().addCrossWindowBlurEnabledListener(
                                                     blurEnabledListener);
                    }

                       @Override
                   public void onViewDetachedFromWindow(View v) {
                       getWindowManager().removeCrossWindowBlurEnabledListener(
                                                      blurEnabledListener);
                     }
           });
       }
       getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
   }

打开和关闭窗口模糊

有两种方法可以允许和禁止窗口模糊。

  1. 从用户界面:

    设置 -> 系统 -> 开发者选项 -> 硬件加速渲染 -> 允许窗口级模糊

  2. 从终端(设备必须植根):

adb shell wm disable-blur 1 # 1 disables window blurs, 0 allows them

如果您的设备能够支持模糊,您只能打开或关闭窗口模糊功能。 (不支持窗口模糊的设备无法启用该功能。)默认情况下,在支持它们的设备上启用模糊。

当您为设备启用模糊时,请考虑诸如省电模式或多媒体隧道之类的其他功能可以禁用它们。当满足所有必要条件时启用模糊 - 它们受到支持,并且没有任何东西禁用它们。要查看模糊功能的当前状态是否为“启用”,请使用adb shell wm disable-blur命令。

验证

为确保您的模糊功能版本按预期工作,请实现 UI 逻辑,以便在blurEnabled更改时重绘 UI 元素(如addCrossWindowBlurEnabledListener报告的那样)。

  1. 打开有模糊的 UI。
  2. 使用从 UI 或 CLI打开和关闭窗口模糊给出的步骤。
  3. 验证 UI 是否按预期更改为模糊的。

故障排除

使用以下内容作为验证期间故障排除的指南。

没有画模糊

  • 通过使用 CLI 或导航到Settings来验证当前是否启用了模糊(并且您的硬件支持它们)。

    1. 使用adb shell wm disable-blur命令,该命令会打印出该设备是否支持模糊以及当前是否启用。
    2. 导航到设置 -> 系统 -> 开发人员选项 -> 硬件加速渲染 -> 允许窗口级模糊。如果您在此处找不到该选项,则您的设备不支持模糊。
  • 确保设置半透明的窗口背景颜色;不透明的窗口背景颜色隐藏(覆盖)模糊区域。

测试设备不支持窗口模糊

  • 在 Android 12 模拟器上测试您的应用程序。要设置 Android 模拟器,请参阅设置 Android 模拟器说明。您使用模拟器创建的任何 Android 虚拟设备都将支持窗口模糊。

没有圆角

  • 通过设置窗口背景可绘制对象 - Window#setBackgroundDrawable来定义圆角。这决定了模糊区域的轮廓。

更新开发者选项不会启用模糊

  • 检查设备是否处于省电模式,是否使用多媒体隧道(用于电视),或者是否有其他原因禁用模糊功能。

背景模糊绘制全屏,不在窗口范围内

  • 确保您的窗口被标记为浮动 - android:windowIsFloating
  • 确保您已设置窗口背景可绘制对象 - Window#setBackgroundDrawable 。这决定了模糊区域的轮廓。

来自监听器的更新不会应用到屏幕上

  • 检查窗口是否被销毁并重新创建,而侦听器正在操作的实例没有更新。侦听器更新可能会应用于旧窗口实例。