在 Android 12 中,您可以使用公用 API 實作視窗模糊效果,例如背景模糊和後方的模糊效果。
視窗模糊處理 (或跨視窗模糊處理) 可用於將特定視窗後方的螢幕畫面模糊處理。有兩種可用於產生不同視覺效果的窗口模糊效果:
「背景模糊」可讓您建立具有模糊背景的視窗,創造磨砂玻璃效果。
模糊處理後方可讓您將 (對話方塊) 視窗後方的整個畫面模糊處理,產生景深效果。
這兩項效果可以單獨或搭配使用,如下圖所示:
a 鍵 |
b |
c |
圖 1. 僅模糊處理背景 (a)、僅模糊處理背景 (b)、模糊處理背景和背景 (c)
視窗模糊功能可跨視窗運作,也就是說,如果視窗後方有其他應用程式,也可以使用這項功能。這項效果與模糊算繪效果不同,後者會模糊處理同一個視窗內的內容。視窗模糊效果可用於對話方塊、底部頁面和其他浮動式視窗。
實作
應用程式開發人員
應用程式開發人員必須提供模糊半徑,才能產生模糊效果。模糊半徑可控制模糊效果的密度,也就是說,半徑越高,模糊效果就越密集。模糊處理值為 0 px 表示不模糊處理。對於後方的模糊效果,半徑 20 像素可以產生良好的視野深度,而背景模糊半徑 80 像素則能產生良好的冰霜玻璃效果。請避免讓模糊範圍大於 150 像素,否則會大幅影響效能。
如要達到想要的模糊效果並提高可讀性,請選擇模糊處理半徑值,搭配半透明色層使用。
背景模糊效果
在浮動視窗使用背景模糊處理功能,建立視窗背景效果,也就是將基礎內容的模糊處理圖片。如要為視窗新增模糊處理的背景,請按照下列步驟操作:
呼叫 Window#setBackgroundBlurRadius(int) 來設定背景模糊半徑。或者,您也可以在視窗主題中設定 R.attr.windowBackgroundBlurRadius。
將 R.attr.windowIsTranslucent 設為 true,讓視窗成為半透明。模糊內容會顯示在視窗介面下方,因此視窗必須是半透明,才能顯示模糊效果。
您可以選擇呼叫 Window#setBackgroundDrawableResource(int),新增半透明顏色的矩形視窗背景可繪項目。或者,在視窗主題中設定 R.attr.windowBackground。
如果是圓角視窗,請設定具有圓角的 ShapeDrawable 做為視窗背景可繪項目,藉此決定模糊區域的圓角。
處理啟用和停用的模糊狀態。詳情請參閱「在應用程式中使用視窗模糊處理的指南」一節。
模糊處理背景
後方的模糊效果會模糊處理視窗後方的整個畫面。這個效果可以藉由模糊處理視窗後方畫面上的任何內容,將使用者的注意力導向視窗內容。
如要模糊處理視窗後方的內容,請按照下列步驟操作:
將
FLAG_BLUR_BEHIND
新增至視窗旗標,啟用背景模糊處理功能。或者,您也可以在視窗主題中設定 R.attr.windowBlurBehindEnabled。呼叫
WindowManager.LayoutParams#setBlurBehindRadius
即可設定模糊效果的背景半徑。或者,您也可以在視窗主題中設定 R.attr.windowBlurBehindRadius。處理啟用和停用模糊功能的狀態。詳情請參閱「在應用程式中使用視窗模糊處理的指南」一節。
在應用程式中使用視窗模糊處理指南
系統是否支援 Windows 模糊效果,取決於下列因素:
Android 版本:窗格模糊處理 API 僅適用於 Android 12 以上版本。查看裝置 SDK 以瞭解 Android 版本。
圖形效能:GPU 效能較低的裝置可能會選擇不支援視窗模糊處理。
系統狀態:系統伺服器可能會在執行階段 (例如省電模式) 時暫時停用視窗模糊處理,同時播放特定類型的影片內容或導致開發人員覆寫設定。
為了讓應用程式適用於各種 Android 版本、裝置和系統狀態,請遵守下列規範:
透過 WindowManager#addCrossWindowBlurEnabledListener 新增事件監聽器,在視窗模糊效果啟用或停用時通知您。此外,請使用
WindowManager#isCrossWindowBlurEnabled
查詢目前是否已啟用窗格模糊處理功能。實作兩個視窗背景版本,以符合啟用或停用視窗模糊效果的狀態。
啟用模糊處理功能時,視窗背景應為半透明,以便顯示模糊效果。在此狀態下,停用模糊處理功能時,視窗內容會與基礎視窗的內容直接重疊,讓重疊視窗變得較不易閱讀。為避免產生這種效果,請在停用視窗模糊處理時,按照下列方式調整應用程式的使用者介面:
針對背景模糊效果,請增加視窗背景可繪項目的 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
驗證
如要驗證應用程式視窗在切換啟用模糊處理和停用模糊處理狀態時,是否有適當的處理方式,請按照下列步驟操作:
開啟有模糊效果的 UI。
開啟或關閉視窗模糊效果,即可啟用或停用視窗模糊效果。
確認視窗 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 模擬器,請參閱「設定 Android 模擬器」一文。您使用模擬器建立的所有 Android 虛擬裝置都支援視窗模糊處理。
無圓角
- 設定視窗背景可繪項目以定義圓角。這個可繪項目會決定模糊處理區域的外框。
更新開發人員選項後無法啟用模糊處理
- 檢查裝置是否處於省電模式,或是否使用多媒體通道。在某些電視裝置上,系統可能也會在影片播放期間停用模糊處理功能。
背景模糊效果繪製為全螢幕,而非在視窗邊界內
勾選 android:windowIsFloating,確保視窗標示為浮動。
確認已設定視窗背景可繪項目。這項設定會決定模糊區域的輪廓。
接聽器的更新內容未套用至畫面
- 監聽器更新可能會套用至舊視窗執行個體。檢查視窗是否遭到刪除,並使用正確的事件監聽器更新重新建立視窗。