ウィンドウブラー

Android 12では、ウィンドウブラー効果(背景のぼかしやぼかしの後ろなど)を実装するためのパブリックAPIを使用できます。コード、開発者向けドキュメント、またはUI表記では、クロスウィンドウブラーとも呼ばれるウィンドウブラーが表示される場合がありますが、クロスウィンドウブラーはウィンドウブラーと同じです。

これらのAPIを使用すると、自分のウィンドウの背後にあるものをすべてぼかすことができます。背景がぼやけたウィンドウを作成してすりガラス効果を作成したり、画面全体がぼやけたウィンドウを表示して被写界深度効果を作成したりできます。 2つの効果を組み合わせることもできます。

背景ぼかしのみ

1

後ろだけをぼかす

2

後ろと背景のぼかし

3

図1.背景のぼかしのみ(1)、背後のぼかしのみ(2)、背景のぼかしと背後のぼかし(3)

ウィンドウブラー機能はウィンドウ間で機能します。つまり、表示しているウィンドウの後ろに別のアプリがある場合にも機能します。これは、同じアプリ内のウィンドウ内のコンテンツをぼかすぼかしレンダリング効果と同じではありません。ウィンドウブラーは、ダイアログやボトムシート、その他のフローティングウィンドウに役立ちます。

この機能は大量のGPUリソ​​ースを使用することに注意することが重要です。そのため、すべてのAndroidデバイスで使用できますが、十分なGPUパワーを備えたデバイスでのみサポートされます。

実装

OEMおよびパートナー

ウィンドウブラーはデフォルトで無効になっています。デバイスでぼかし機能を有効にするには、次の手順を実行します。

  • デバイスが追加のGPU負荷を処理できることを確認します。ブラー操作はコストがかかり、ローエンドデバイスでは、フレームのドロップが発生する可能性があります。十分なGPUパワーを備えたデバイスでのみこれを有効にしてください。
  • librenderengineがぼかしロジックを実装していることを確認します-デフォルトのAndroid12レンダリングエンジンは実装していますが、カスタムレンダリングエンジンはぼかしロジック自体を実装する必要があります。
  • 次のサーフェスフリンガーsyspropを設定して、ブラーを有効にします。
# enable surface flinger window blurs
PRODUCT_PROPERTY_OVERRIDES += \
       ro.surface_flinger.supports_background_blur=1

サードパーティの開発者

サンプルコードについては、「例とソース」セクションを参照してください。ウィンドウブラーは、システムサーバーによって実行時に無効にできます。したがって、アプリはフォールバックのぼやけのないバージョンを提供する必要があります。そうしないと、ぼかしが無効になっているためにぼかしがレンダリングされない場合、ウィンドウの背景が透明になりすぎて、ウィンドウ内のコンテンツが判読できなくなる可能性があります。アプリがフォールバックアプリバージョンを提供していない場合は、UIがブラーを有効にした場合とブラーを無効にした場合の両方で機能することを確認してください。これらは、ブラーをいつでも無効にできる3つの条件です。

  1. デバイスはAndroid11以下を実行しています。ウィンドウブラーはAndroid12以降のデバイスでのみ使用できるため、アプリはAndroid11以下を実行しているデバイスにフォールバックのブラーレスエクスペリエンスの代替手段を実装する必要があります。
  2. デバイスは高価であるためウィンドウブラーをサポートしていません。そのため、ローエンドデバイスはレンダリング時にフレームをドロップする可能性があります。このような場合、アプリはフォールバックのぼかしのないエクスペリエンスを提供する必要があります。
  3. システムサーバー(たとえば、バッテリーセーバーモード中、または開発者設定またはトンネルモードによる)は、実行時にブラーを無効にします。

上記のポイント23はどちらも、 WindowManager.addCrossWindowBlurEnabledListenerに登録されているリスナーによって報告されます。アプリがblurAPIを使用している場合、blurが有効な状態とblurが無効な状態に異なる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);
   }

ウィンドウブラーのオンとオフを切り替える

ウィンドウのぼかしを許可および禁止する方法は2つあります。

  1. UIから:

    [設定]->[システム]->[開発者向けオプション]->[ハードウェアアクセラレーションレンダリング]->[ウィンドウレベルのぼかしを許可する]

  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を使用するか、 [設定]に移動して、ぼかしが現在有効になっていること(およびハードウェアがぼかしをサポートしていること)を確認します。

    1. adb shell wm disable-blurコマンドを使用します。このコマンドは、そのデバイスでブラーがサポートされているかどうか、および現在有効になっているかどうかを出力します。
    2. [設定]->[システム]->[開発者向けオプション]->[ハードウェアアクセラレーションレンダリング]->[ウィンドウレベルのぼかしを許可]に移動します。そこにオプションが見つからない場合、ブラーはデバイスでサポートされていません。
  • 半透明のウィンドウの背景色を設定していることを確認してください。不透明なウィンドウの背景色は、ぼやけた領域を非表示(カバー)します。

テストデバイスはウィンドウブラーをサポートしていません

  • Android12エミュレーターでアプリケーションをテストします。 Androidエミュレーターをセットアップするには、「Androidエミュレーターのセットアップ」の説明を参照してください。エミュレーターで作成するAndroid仮想デバイスはすべて、ウィンドウブラーをサポートします。

角が丸くない

  • ウィンドウの背景ドローアブルを設定して、丸みを帯びたコーナーを定義しますWindow#setBackgroundDrawable 。これにより、ぼかし領域の輪郭が決まります。

開発者オプションを更新してもブラーは有効になりません

  • デバイスがバッテリー節約モードになっているかどうか、マルチメディアトンネリング(TV用)を使用しているかどうか、または他の何かがブラー機能を無効にしていないかどうかを確認します。

ウィンドウの境界内ではなく、フルスクリーンで描画された背景のぼかし

  • ウィンドウがフローティングとしてマークされていることを確認しますandroid:windowIsFloating
  • ウィンドウの背景ドローアブルを設定していることを確認してくださいWindow#setBackgroundDrawable 。これにより、ぼかし領域の輪郭が決まります。

リスナーからの更新は画面に適用されません

  • リスナーによって操作されているインスタンスが更新されていないときに、ウィンドウが破棄されて再作成されていないかどうかを確認します。リスナーの更新が古いウィンドウインスタンスに適用されている可能性があります。