序文
Android 12 では、背景のぼかしや背面の画面のぼかしなど、ウィンドウのぼかし効果を実装するためのパブリック API がいくつかあります。Window Blur または Cross-Window Blur は、特定のウィンドウの背後にある画面をぼかすために使用されます。
さまざまな視覚効果を実現するために使用できるウィンドウのぼかし方法は 2 つあります。
-
背景のぼかし: 背景をぼかした窓を作成し、ぼかした領域が窓となる曇りガラスの効果を作成するために使用できます。
-
背後をぼかし: ウィンドウの後ろの画面全体をぼかし (ダイアログ)、ぼかした領域が画面全体になる被写界深度効果を作成するために使用できます。
これら 2 つの効果は、次の図に示すように、単独で使用することも、組み合わせて使用することもできます。
上記の 3 つのレンダリングは、Google が提供する公式のレンダリングです。
(a) 背景ぼかしのみ(後ろぼかし)
(b) 画面後ろのみぼかし(後ろぼかし)
(c) 背景ぼかしと画面後ろぼかし(背景ぼかし)+(後ろぼかし)
Android 12 で提供されるウィンドウぼかし機能は複数のウィンドウで使用できます。つまり、この機能はウィンドウの背後に他のアプリケーションがある場合にも機能しますが、ウィンドウをまたいだぼかしは非常にリソースを消費します。この効果は、同じウィンドウ内のコンテンツをぼかすぼかしレンダリング効果とは異なります。ウィンドウのぼかしは、ダイアログ、下部のアクション バー、その他のフローティング ウィンドウに役立ちます。
公式の説明を添付します
1. ウィンドウの背景をぼかす
ウィンドウの背景をぼかすには主に以下の操作があります。
1. Window#setBackgroundBlurRadius(int) を呼び出して、背景のぼかし半径を設定します。または、ウィンドウ テーマで R.attr.windowBackgroundBlurRadius を設定します。
2. R.attr.windowIsTranslucent を true に設定して、ウィンドウを半透明にします。ぼかしはウィンドウ Surface の下に描画されるため、ぼかし効果を表示するにはウィンドウを半透明にする必要があります。
3. (オプション) Window#setBackgroundDrawableResource(int) を呼び出して、半透明色の長方形のウィンドウ背景ドローアブルを追加します。あるいは、ウィンドウテーマで R.attr.windowBackground を設定します。
4. 角の丸いウィンドウの場合、ぼかし領域の角の丸い部分は、角の丸い ShapeDrawable をウィンドウの背景の描画可能オブジェクトとして設定することによって決定できます。
5. 難読化の有効化と無効化の状態。
2. 窓の後ろのスクリーンをぼかします
ウィンドウの後ろの画面をぼかす必要がある場合は、主に次の操作があります。
1. FLAG_BLUR_BEHIND をウィンドウ フラグに追加して、「画面の後ろをぼかす」を有効にします。または、ウィンドウ テーマで R.attr.windowBlurBehindEnabled を設定します。
2. WindowManager.LayoutParams#setBlurBehindRadius を呼び出して、「画面裏のぼかし」の半径を設定します。あるいは、ウィンドウテーマで R.attr.windowBlurBehindRadius を設定します。
3. (オプション) 補色を選択します。Window#setDimAmount(浮動小数点数)
4. 難読化の有効化と無効化の状態を処理します。
3. レンダリング
Android 12 でウィンドウの背景とウィンドウの背後の画面をぼかすための API については上で簡単に紹介しました。以下は実装の最終レンダリングです。ダイアログとアクティビティを使用して、現在の背景のみ、背面の画面のみをぼかします。と現在の背景を同時に表示します。そして、後画面によく使用される 3 つのガウスぼかし効果を表示します。
1. ダイアログを使用してガウスぼかし効果を実現します
。 2. アクティビティを使用してガウスぼかし効果を実現します。
4番目、特定のコード
最後に、完全なプロジェクト コードを見てみましょう。
1.gradle設定ファイル、AndroidManifest.xmlマニフェストファイル、style.xmlスタイルファイル
plugins {
id 'com.android.application'
}
android {
namespace 'com.example.myapplication'
compileSdk 32
defaultConfig {
applicationId "com.example.myapplication"
minSdk 31
targetSdk 32
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}
}
dependencies {
implementation 'androidx.appcompat:appcompat:1.4.1'
implementation 'com.google.android.material:material:1.5.0'
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<application
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="com.example.blurapplication.MainActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.example.blurapplication.BlurActivity"
android:exported="false"
android:theme="@style/BlurActivityTheme" />
</application>
</manifest>
<resources>
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
</style>
<!--高斯模糊activity-->
<style name="BlurActivityTheme" parent="Theme.MaterialComponents.Dialog">
<item name="android:windowIsTranslucent">true</item>
</style>
<!--高斯模糊Dialog-->
<style name="BlurDialogTheme" parent="Theme.MaterialComponents.Dialog">
<item name="android:windowIsTranslucent">true</item>
</style>
</resources>
2. MainActivity.java および activity_main.xml レイアウト ファイル
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.btn_dialog_blur_background).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//仅模糊背景
BlurDialog dialog = new BlurDialog(MainActivity.this, BlurDialog.BLUR_TYPE_BLUR_BACKGROUND);
dialog.show();
}
});
findViewById(R.id.btn_dialog_blur_behind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//仅模糊后方屏幕
BlurDialog dialog = new BlurDialog(MainActivity.this, BlurDialog.BLUR_TYPE_BLUR_BEHIND);
dialog.show();
}
});
findViewById(R.id.btn_dialog_blur_background_and_behind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//同时模糊背景和后方屏幕
BlurDialog dialog = new BlurDialog(MainActivity.this, BlurDialog.BLUR_TYPE_BLUR_BACKGROUND_AND_BEHIND);
dialog.show();
}
});
findViewById(R.id.btn_activity_blur_background).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//仅模糊背景
Intent intent = new Intent(MainActivity.this, BlurActivity.class);
intent.putExtra(BlurActivity.EXTRA_KEY_BLUR_TYPE, BlurActivity.BLUR_TYPE_BLUR_BACKGROUND);
startActivity(intent);
}
});
findViewById(R.id.btn_activity_blur_behind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//仅模糊后方屏幕
Intent intent = new Intent(MainActivity.this, BlurActivity.class);
intent.putExtra(BlurActivity.EXTRA_KEY_BLUR_TYPE, BlurActivity.BLUR_TYPE_BLUR_BEHIND);
startActivity(intent);
}
});
findViewById(R.id.btn_activity_blur_background_and_behind).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//同时模糊背景和后方屏幕
Intent intent = new Intent(MainActivity.this, BlurActivity.class);
intent.putExtra(BlurActivity.EXTRA_KEY_BLUR_TYPE, BlurActivity.BLUR_TYPE_BLUR_BACKGROUND_AND_BEHIND);
startActivity(intent);
}
});
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@mipmap/img"
android:orientation="vertical"
tools:context="com.example.blurapplication.MainActivity">
<Button
android:id="@+id/btn_dialog_blur_background"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="模糊Dialog的背景"
android:textAllCaps="false"
android:textSize="24sp" />
<Button
android:id="@+id/btn_dialog_blur_behind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="模糊Dialog后方屏幕"
android:textAllCaps="false"
android:textSize="24sp" />
<Button
android:id="@+id/btn_dialog_blur_background_and_behind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="同时模糊Dialog的背景和后方屏幕"
android:textAllCaps="false"
android:textSize="24sp" />
<Button
android:id="@+id/btn_activity_blur_background"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="模糊Activity的背景"
android:textAllCaps="false"
android:textSize="24sp" />
<Button
android:id="@+id/btn_activity_blur_behind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="模糊Activity后方屏幕"
android:textAllCaps="false"
android:textSize="24sp" />
<Button
android:id="@+id/btn_activity_blur_background_and_behind"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="同时模糊Activity的背景和后方屏幕"
android:textAllCaps="false"
android:textSize="24sp" />
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:background="#FFF"
android:gravity="center"
android:text="高斯模糊效果测试"
android:textSize="46sp" />
</LinearLayout>
3. BackgroundBlurDialog.java および Dialog_background_blur.xml レイアウト ファイル
public class BlurDialog extends AlertDialog {
private Window mWindow;
//窗口背景高斯模糊程度,数值越高越模糊且越消耗性能
private final int mBackgroundBlurRadius = 90;
//窗口周边背景高斯模糊程度
private final int mBlurBehindRadius = 20;
//根据窗口高斯模糊功能是否开启来设置窗口周边暗色的程度
private final float mDimAmountWithBlur = 0f;
private final float mDimAmountNoBlur = 0.4f;
// 根据窗口高斯模糊功能是否开启来为窗口设置不同的不透明度
private final int mWindowBackgroundAlphaWithBlur = 170;
private final int mWindowBackgroundAlphaNoBlur = 255;
//使用一个矩形drawable文件作为窗口背景,这个矩形的轮廓和圆角确定了窗口高斯模糊的区域
private Drawable mWindowBackgroundDrawable;
/**
* 高斯模糊的类型
* 0代表只模糊背景
* 1代表之模糊后方屏幕
* 2代表同时模糊背景和后方屏幕
*/
private int mBlurType = 0;
public static final int BLUR_TYPE_BLUR_BACKGROUND = 0;
public static final int BLUR_TYPE_BLUR_BEHIND = 1;
public static final int BLUR_TYPE_BLUR_BACKGROUND_AND_BEHIND = 2;
public BlurDialog(@NonNull Context context, int blurType) {
super(context, R.style.BlurDialogTheme);
mBlurType = blurType;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.dialog_blur);
initBlur();
}
private void initBlur() {
mWindow = getWindow();
//替换window默认的背景
mWindowBackgroundDrawable = getContext().getDrawable(R.drawable.window_background);
getWindow().setBackgroundDrawable(mWindowBackgroundDrawable);
//注册一个监听者去监听窗口UI视图是否可见以便调整窗口高斯模糊功能是否开启
setupWindowBlurListener();
//允许背景模糊,也可以通过样式属性R.attr#windowBlurBehindEnabled来实现
getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
// 允许背景变暗,也可以通过样式属性R.attr#backgroundDimEnabled来实现
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
}
/**
* 设置一个窗口视图状态监听者,监听窗口视图是否可见以便是否更新窗口模糊的状态
*/
private void setupWindowBlurListener() {
Consumer<Boolean> windowBlurEnabledListener = this::updateWindowForBlurs;
getWindow().getDecorView().addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
@Override
public void onViewAttachedToWindow(View v) {
mWindow.getWindowManager().addCrossWindowBlurEnabledListener(windowBlurEnabledListener);
}
@Override
public void onViewDetachedFromWindow(View v) {
mWindow.getWindowManager().removeCrossWindowBlurEnabledListener(windowBlurEnabledListener);
}
});
}
/**
* 更新窗口的高斯模糊效果
*
* @param blursEnabled
*/
private void updateWindowForBlurs(boolean blursEnabled) {
if (mBlurType == BLUR_TYPE_BLUR_BACKGROUND) {
//仅模糊背景
mWindowBackgroundDrawable.setAlpha(blursEnabled ? mWindowBackgroundAlphaWithBlur : mWindowBackgroundAlphaNoBlur);//调整背景的透明度
getWindow().setDimAmount(blursEnabled ? mDimAmountWithBlur : mDimAmountNoBlur);//调整背景周边昏暗的程度
getWindow().setBackgroundBlurRadius(mBackgroundBlurRadius);//设置背景模糊程度
return;
}
if (mBlurType == BLUR_TYPE_BLUR_BEHIND) {
//仅模糊后方屏幕
getWindow().setDimAmount(blursEnabled ? mDimAmountWithBlur : mDimAmountNoBlur);//调整背景周边昏暗的程度
getWindow().getAttributes().setBlurBehindRadius(mBlurBehindRadius);//设置背景周边模糊程度
getWindow().setAttributes(getWindow().getAttributes());//让上面的高斯模糊效果生效
return;
}
//同时模糊背景和后方屏幕
mWindowBackgroundDrawable.setAlpha(blursEnabled ? mWindowBackgroundAlphaWithBlur : mWindowBackgroundAlphaNoBlur);//调整背景的透明度
getWindow().setBackgroundBlurRadius(mBackgroundBlurRadius);//设置背景模糊程度
getWindow().setDimAmount(blursEnabled ? mDimAmountWithBlur : mDimAmountNoBlur);//调整背景周边昏暗的程度
getWindow().getAttributes().setBlurBehindRadius(mBlurBehindRadius);//设置背景周边模糊程度
getWindow().setAttributes(getWindow().getAttributes());//让上面的高斯模糊效果生效
}
}
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="20dp" />
<solid android:color="#AAAAAA" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="200dp"
android:layout_height="200dp"
android:gravity="center"
android:text="你好,我是高斯模糊Dialog"
android:textAllCaps="false"
android:textSize="30sp" />
</LinearLayout>
4. BackgroundBlurActivity.java および activity_background_blur.xml レイアウト ファイル
public class BlurActivity extends Activity {
public static final String EXTRA_KEY_BLUR_TYPE = "blur_type";
public static final int BLUR_TYPE_BLUR_BACKGROUND = 0;
public static final int BLUR_TYPE_BLUR_BEHIND = 1;
public static final int BLUR_TYPE_BLUR_BACKGROUND_AND_BEHIND = 2;
//窗口背景高斯模糊程度,数值越高越模糊且越消耗性能
private final int mBackgroundBlurRadius = 90;
//窗口周边背景高斯模糊程度
private final int mBlurBehindRadius = 20;
//根据窗口高斯模糊功能是否开启来设置窗口周边暗色的程度
private final float mDimAmountWithBlur = 0f;
private final float mDimAmountNoBlur = 0.4f;
// 根据窗口高斯模糊功能是否开启来为窗口设置不同的不透明度
private final int mWindowBackgroundAlphaWithBlur = 170;
private final int mWindowBackgroundAlphaNoBlur = 255;
//使用一个矩形drawable文件作为窗口背景,这个矩形的轮廓和圆角确定了窗口高斯模糊的区域
private Drawable mWindowBackgroundDrawable;
/**
* 高斯模糊的类型
* 0代表只模糊背景
* 1代表之模糊后方屏幕
* 2代表同时模糊背景和后方屏幕
*/
private int mBlurType = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mBlurType = getIntent().getIntExtra(EXTRA_KEY_BLUR_TYPE, BLUR_TYPE_BLUR_BACKGROUND);
setContentView(R.layout.activity_blur);
initBlur();
}
private void initBlur() {
//替换window默认的背景
mWindowBackgroundDrawable = getDrawable(R.drawable.window_background);
getWindow().setBackgroundDrawable(mWindowBackgroundDrawable);
//注册一个监听者去监听窗口UI视图是否可见以便调整窗口高斯模糊功能是否开启
setupWindowBlurListener();
//允许背景模糊,也可以通过样式属性R.attr#windowBlurBehindEnabled来实现
getWindow().addFlags(WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
// 允许背景变暗,也可以通过样式属性R.attr#backgroundDimEnabled来实现
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
findViewById(R.id.ll_content).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
/**
* 设置一个窗口视图状态监听者,监听窗口视图是否可见以便是否更新窗口模糊的状态
*/
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);
}
});
}
/**
* 更新窗口的高斯模糊效果
*
* @param blursEnabled
*/
private void updateWindowForBlurs(boolean blursEnabled) {
if (mBlurType == BLUR_TYPE_BLUR_BACKGROUND) {
//仅模糊背景
mWindowBackgroundDrawable.setAlpha(blursEnabled ? mWindowBackgroundAlphaWithBlur : mWindowBackgroundAlphaNoBlur);//调整背景的透明度
getWindow().setDimAmount(blursEnabled ? mDimAmountWithBlur : mDimAmountNoBlur);//调整背景周边昏暗的程度
getWindow().setBackgroundBlurRadius(mBackgroundBlurRadius);//设置背景模糊程度
return;
}
if (mBlurType == BLUR_TYPE_BLUR_BEHIND) {
//仅模糊后方屏幕
getWindow().setDimAmount(blursEnabled ? mDimAmountWithBlur : mDimAmountNoBlur);//调整背景周边昏暗的程度
getWindow().getAttributes().setBlurBehindRadius(mBlurBehindRadius);//设置背景周边模糊程度
getWindow().setAttributes(getWindow().getAttributes());//让上面的高斯模糊效果生效
return;
}
//同时模糊背景和后方屏幕
mWindowBackgroundDrawable.setAlpha(blursEnabled ? mWindowBackgroundAlphaWithBlur : mWindowBackgroundAlphaNoBlur);//调整背景的透明度
getWindow().setBackgroundBlurRadius(mBackgroundBlurRadius);//设置背景模糊程度
getWindow().setDimAmount(blursEnabled ? mDimAmountWithBlur : mDimAmountNoBlur);//调整背景周边昏暗的程度
getWindow().getAttributes().setBlurBehindRadius(mBlurBehindRadius);//设置背景周边模糊程度
getWindow().setAttributes(getWindow().getAttributes());//让上面的高斯模糊效果生效
}
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:id="@+id/ll_content"
android:orientation="vertical">
<TextView
android:layout_width="300dp"
android:layout_height="400dp"
android:gravity="center"
android:text="你好,我是高斯模糊Activity"
android:textAllCaps="false"
android:textSize="30sp" />
</LinearLayout>
5.デバイスがガウスぼかしをサポートしているかどうかを確認します
デバイスでガウスぼかし機能が有効になっていない場合、上記のコードはガウスぼかし効果を実現できません。Android12 以降のデバイスがウィンドウぼかしをサポートしているかどうか、またウィンドウぼかしが現在有効かどうかを確認するには、次の 2 つの方法があります。
1. adb コマンドラインを使用します。
adb shell wm disable-blur
1) デバイスが対応しており、ガウスブラー機能スイッチがオンになっている
Blur supported on device: true
Blur enabled: true
2) デバイスがサポートしていない
Blur supported on device: false
Blur enabled: false
2. 設定を入力します
1) 設定 -> システム -> 開発者向けオプション -> ハードウェア アクセラレーション レンダリング -> ウィンドウ レベルのブラーを許可
2) [設定] にオプションが見つからない場合、お使いのデバイスはウィンドウぼかしをサポートしていません。
6. デバイスのガウスぼかし機能をオンにします
1. システムコンパイルファイルを変更して、ガウスブラー機能をオンにします。
ガウス ブラー関数は GPU リソースを大量に消費するため、Android12 システム ソース コードはコンパイル時にデフォルトでガウス ブラー関数をオフにします。
デバイス/qcom/msmnile_gvmq/msmnile_gvmq.mk
# enable surface flinger window blurs
PRODUCT_PROPERTY_OVERRIDES += \
ro.surface_flinger.supports_background_blur=1
2. システムをroot化した後、システムのソースコードファイルを直接変更します。
ベンダー/build.prop
ro.surface_flinger.supports_background_blur=1