視窗模糊處理

在 Android 12 中,您可以使用公開 API 實作視窗模糊效果,例如背景模糊和模糊背景。

視窗模糊處理或跨視窗模糊處理可用於模糊指定視窗後方的畫面。有兩種可用於產生不同視覺效果的窗口模糊效果:

  • 背景模糊功能可讓您建立帶有模糊背景的視窗,產生磨砂玻璃效果。

  • 模糊處理後方可讓您將 (對話方塊) 視窗後方的整個畫面模糊處理,產生景深效果。

這兩種效果可以單獨使用或合併使用,如以下圖所示:

僅限背景模糊

a 鍵

僅模糊處理背景

b

模糊處理背景和背景模糊效果

c

圖 1. 僅模糊處理背景 (a)、僅模糊處理背景 (b)、模糊處理背景和背景 (c)

視窗模糊處理功能適用於各個視窗,也就是說,即使視窗後方有其他應用程式,這項功能也能正常運作。這項效果與模糊算繪效果不同,後者會模糊處理同一個視窗的內容。視窗模糊效果可用於對話方塊、底部功能表和其他浮動式視窗。

實作

應用程式開發人員

應用程式開發人員必須提供模糊半徑,才能產生模糊效果。模糊半徑可控制模糊效果的密度,也就是說,半徑越高,模糊效果就越密集。模糊處理值為 0 px 表示不模糊處理。若要模糊處理背景,半徑為 20 px 可產生良好的景深效果,而背景模糊半徑為 80 px 可產生良好的磨砂玻璃效果。請勿使用超過 150 像素的模糊處理半徑,否則會大幅影響效能。

如要獲得所需的模糊效果並提升可讀性,請選擇模糊半徑值,並搭配半透明色彩層。

背景模糊效果

在浮動視窗上使用背景模糊處理功能,即可產生視窗背景效果,也就是將底層內容模糊處理。如要為視窗新增模糊處理的背景,請按照下列步驟操作:

  1. 呼叫 Window#setBackgroundBlurRadius(int) 來設定背景模糊半徑。或者,您也可以在視窗主題中設定 R.attr.windowBackgroundBlurRadius

  2. R.attr.windowIsTranslucent 設為 true,即可讓視窗呈現半透明效果。模糊效果會在視窗表面下方繪製,因此視窗必須是半透明,才能讓模糊效果顯示出來。

  3. 您可以選擇呼叫 Window#setBackgroundDrawableResource(int),新增半透明顏色的矩形視窗背景可繪項目。或者,在視窗主題中設定 R.attr.windowBackground

  4. 如果是圓角視窗,請將具有圓角ShapeDrawable 設為視窗背景可繪項目,藉此決定模糊區域的圓角。

  5. 處理啟用和停用的模糊狀態。詳情請參閱「在應用程式中使用窗格模糊處理功能的規範」一節。

模糊處理背景

背景模糊處理會模糊處理視窗後方的整個畫面。這個效果會模糊處理視窗後方螢幕上的所有內容,引導使用者將注意力集中在視窗內容上。

如要模糊處理視窗後方的內容,請按照下列步驟操作:

  1. FLAG_BLUR_BEHIND 新增至視窗旗標,啟用背景模糊效果。或者,在視窗主題中設定 R.attr.windowBlurBehindEnabled

  2. 呼叫 WindowManager.LayoutParams#setBlurBehindRadius 以設定模糊效果的背景半徑。或者,您也可以在視窗主題中設定 R.attr.windowBlurBehindRadius

  3. 視需要選擇補充的dim amount

  4. 處理啟用和停用的模糊狀態。詳情請參閱「在應用程式中使用窗格模糊處理功能的規範」一節。

在應用程式中使用模糊視窗功能的規範

系統是否支援窗格模糊效果取決於下列因素:

  • Android 版本:窗格模糊處理 API 僅適用於 Android 12 以上版本。查看裝置 SDK 的 Android 版本。

  • 圖形效能:GPU 效能較低的裝置可能會選擇不支援視窗模糊處理。

  • 系統狀態:系統伺服器可能會在執行階段暫時停用視窗模糊處理效果,例如在省電模式下、播放特定類型的影片內容,或因開發人員覆寫而停用。

為了讓應用程式在各個 Android 版本、裝置和系統狀態下皆能正常運作,請遵循下列規範:

  • 透過 WindowManager#addCrossWindowBlurEnabledListener 新增事件監聽器,以便在啟用或停用視窗模糊處理時通知您。此外,請使用 WindowManager#isCrossWindowBlurEnabled 查詢目前是否已啟用視窗模糊處理功能。

  • 為視窗背景實作兩個版本,以便因應視窗模糊效果的啟用或停用狀態。

    啟用模糊處理時,視窗背景應為半透明,以便顯示模糊效果。在這種狀態下,當模糊處理功能停用時,視窗內容會直接與底層視窗的內容重疊,導致重疊視窗不易辨識。為避免產生這種效果,請在停用視窗模糊處理時,按照下列方式調整應用程式的 UI:

    • 如要模糊處理背景,請提高視窗背景可繪項目的 alpha 值,使其更不透明。

    • 如要模糊處理背景,請新增較高模糊程度的模糊圖層。

模糊處理背景和背景模糊處理的範例

本節提供活動的實際範例,說明如何同時使用模糊背景和背景模糊效果。

以下 MainActivity.java 範例是對話方塊,其中背景模糊半徑為 20 像素,背景模糊半徑為 80 像素。它具有圓角,是在視窗背景可繪項目的 XML 中定義。它可正確處理不同的 Android 版本、不同的裝置 (可能不支援視窗模糊處理效果),以及啟用或停用執行階段模糊處理效果的變更。它會調整視窗背景可繪項目的 alpha 值和視窗調暗量,確保對話方塊內容在任何情況下都能清楚顯示。

public class MainActivity extends Activity {

    private final int mBackgroundBlurRadius = 80;
    private final int mBlurBehindRadius = 20;

    // We set a different dim amount depending on whether window blur is enabled or disabled
    private final float mDimAmountWithBlur = 0.1f;
    private final float mDimAmountNoBlur = 0.4f;

    // We set a different alpha depending on whether window blur is enabled or disabled
    private final int mWindowBackgroundAlphaWithBlur = 170;
    private final int mWindowBackgroundAlphaNoBlur = 255;

    // Use a rectangular shape drawable for the window background. The outline of this drawable
    // dictates the shape and rounded corners for the window background blur area.
    private Drawable mWindowBackgroundDrawable;

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

        mWindowBackgroundDrawable = getDrawable(R.drawable.window_background);
        getWindow().setBackgroundDrawable(mWindowBackgroundDrawable);

        if (buildIsAtLeastS()) {
            // Enable blur behind. This can also be done in xml with R.attr#windowBlurBehindEnabled
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);

            // Register a listener to adjust window UI whenever window blurs are enabled/disabled
            setupWindowBlurListener();
        } else {
            // Window blurs are not available prior to Android S
            updateWindowForBlurs(false /* blursEnabled */);
        }

        // Enable dim. This can also be done in xml, see R.attr#backgroundDimEnabled
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
    }

    /**
     * Set up a window blur listener.
     *
     * Window blurs might be disabled at runtime in response to user preferences or system states
     * (e.g. battery saving mode). WindowManager#addCrossWindowBlurEnabledListener allows to
     * listen for when that happens. In that callback we adjust the UI to account for the
     * added/missing window blurs.
     *
     * For the window background blur we adjust the window background drawable alpha:
     *     - lower when window blurs are enabled to make the blur visible through the window
     *       background drawable
     *     - higher when window blurs are disabled to ensure that the window contents are readable
     *
     * For window blur behind we adjust the dim amount:
     *     - higher when window blurs are disabled - the dim creates a depth of field effect,
     *       bringing the user's attention to the dialog window
     *     - lower when window blurs are enabled - no need for a high alpha, the blur behind is
     *       enough to create a depth of field effect
     */
    @RequiresApi(api = Build.VERSION_CODES.S)
    private void setupWindowBlurListener() {
        Consumer<Boolean> windowBlurEnabledListener = this::updateWindowForBlurs;
        getWindow().getDecorView().addOnAttachStateChangeListener(
                new View.OnAttachStateChangeListener() {
                    @Override
                    public void onViewAttachedToWindow(View v) {
                        getWindowManager().addCrossWindowBlurEnabledListener(
                                windowBlurEnabledListener);
                    }

                    @Override
                    public void onViewDetachedFromWindow(View v) {
                        getWindowManager().removeCrossWindowBlurEnabledListener(
                                windowBlurEnabledListener);
                    }
                });
    }

    private void updateWindowForBlurs(boolean blursEnabled) {
        mWindowBackgroundDrawable.setAlpha(blursEnabled && mBackgroundBlurRadius > 0 ?
                mWindowBackgroundAlphaWithBlur : mWindowBackgroundAlphaNoBlur);
        getWindow().setDimAmount(blursEnabled && mBlurBehindRadius > 0 ?
                mDimAmountWithBlur : mDimAmountNoBlur);

        if (buildIsAtLeastS()) {
            // Set the window background blur and blur behind radii
            getWindow().setBackgroundBlurRadius(mBackgroundBlurRadius);
            getWindow().getAttributes().setBlurBehindRadius(mBlurBehindRadius);
            getWindow().setAttributes(getWindow().getAttributes());
        }
    }

    private static boolean buildIsAtLeastS() {
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
    }
}

為視窗建立圓角,我們在 res/drawable/window_background.xml 中將視窗背景定義為 ShapeDrawable,並為圓角設定半徑 20 dp,如下所示:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
    <corners android:radius="20dp"/>
    <solid android:color="#AAAAAA"/>
</shape>

視窗模糊處理會模糊處理活動下方視窗的內容。模糊處理過的圖片會在下方繪製此活動視窗,因此活動視窗必須是半透明,才能讓模糊處理過的圖片顯示出來。為了讓視窗呈現半透明效果,我們會在活動主題中設定 R.attr.windowIsTranslucent,如下所示:

<style name="Theme.BlurryDialog" parent="Theme.MaterialComponents.Dialog">
    <item name="android:windowIsTranslucent">true</item>
</style>

原始設備製造商 (OEM) 和合作夥伴

如要在裝置上使用窗格模糊處理功能,原始設備製造商 (OEM) 必須宣告裝置支援窗格模糊處理功能。

如要確認裝置是否支援窗格模糊處理效果,請按照下列步驟操作:

  • 確認裝置可處理額外的 GPU 負載。低階裝置可能無法處理額外負載,這可能會導致影格遺漏。請只在 GPU 效能足夠的測試裝置上啟用視窗模糊效果。

  • 如果您使用自訂轉譯引擎,請確認轉譯引擎實作模糊處理邏輯。預設的 Android 12 轉譯引擎會在 BlurFilter.cpp 中實作模糊處理邏輯。

確認裝置支援視窗模糊處理後,請設定下列表面投擲器 sysprop

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

驗證

如要驗證應用程式視窗在切換啟用模糊處理和停用模糊處理狀態時,是否有適當的處理方式,請按照下列步驟操作:

  1. 開啟有模糊效果的 UI。

  2. 開啟或關閉視窗模糊處理功能,啟用或停用視窗模糊處理功能。

  3. 確認視窗 UI 是否如預期地變更為模糊狀態,並從模糊狀態變回。

開啟及關閉視窗模糊效果

如要測試視窗 UI 如何搭配視窗模糊效果算繪,請使用下列任一方法啟用或停用模糊效果:

  • 透過「開發人員選項」:

    「設定」->「系統」->「開發人員選項」->「硬體加速轉譯」->「允許視窗級模糊效果」

  • 在已解鎖裝置的終端機中:

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

如要檢查 Android 12 以上版本裝置是否支援視窗模糊處理效果,以及目前是否已啟用視窗模糊處理效果,請在已 Root 的裝置上執行 adb shell wm disable-blur

疑難排解

請參考下列指南,在驗證期間排解問題。

未繪製模糊效果

  • 確認目前已啟用模糊處理功能,且硬體支援此功能。請參閱「開啟或關閉視窗模糊處理功能」一文。

  • 請務必設定半透明視窗背景顏色。不透明視窗背景顏色會隱藏模糊區域。

測試裝置不支援視窗模糊處理

  • 在 Android 12 模擬器上測試應用程式。如要設定 Android Emulator,請參閱「設定 Android Emulator」。您使用模擬器建立的任何 Android 虛擬裝置都支援視窗模糊處理。

無圓角

更新開發人員選項後無法啟用模糊處理

  • 檢查裝置是否處於省電模式,或是否使用多媒體通道。在某些電視裝置上,系統可能也會在影片播放期間停用視窗模糊處理功能。

背景模糊效果繪製為全螢幕,而非在視窗邊界內

接聽器的更新內容未套用至畫面

  • 事件監聽器更新可能會套用至舊的視窗例項。檢查是否會在使用正確的事件監聽器更新後,摧毀並重新建立視窗。