Android12 window blur (1) Realize Gaussian blur effect in Activity and Dialog

foreword

In Android 12, there are some public APIs for implementing window blurring effects such as background blurring and blurring the rear screen. Window Blur or Cross-Window Blur is used to blur the screen behind a given window.
There are two window blurring methods that can be used to achieve different visual effects:

  • Background blur: Can be used to create windows with a blurred background, creating a frosted glass effect, where the blurred area is the window.

  • Blur behind: Can be used to blur (dialog) the entire screen behind the window, creating a depth of field effect, where the blurred area is the entire screen.

These two effects can be used alone or in combination, as shown in the image below:

Realization of window blur function
The three renderings above are official renderings provided by Google:

(a) Background blur only (Blur behind)
(b) Blur only behind the screen (Blur behind)
(c) Background blur and blur behind the screen (Background blur) + (Blur behind)

The window blurring function provided by Android 12 can be used across windows, which means that this function can also work when there are other applications behind the window, but cross-window blurring is very resource-intensive. This effect is different from the blur rendering effect, which blurs content inside the same window. Window blurring is useful for dialogs, bottom action bars, and other floating windows.

Attach the official description

1. Blur the window background

To blur the background of the window, there are mainly the following operations:

1. Call Window#setBackgroundBlurRadius(int) to set the background blur radius. Alternatively, set R.attr.windowBackgroundBlurRadius in the window theme.

2. Set R.attr.windowIsTranslucent to true to make the window translucent. Blurring is drawn under the window Surface, so the window must be semi-transparent to show the blurring effect.

3. (Optional) Call Window#setBackgroundDrawableResource(int) to add a rectangular window background drawable with a translucent color. Alternatively, set R.attr.windowBackground in the window theme.

4. For a window with rounded corners, the rounded corners of the blurred area can be determined by setting the ShapeDrawable with rounded corners as the window background drawable object.

5. The state of enabling and disabling obfuscation.

2. Blur the screen behind the window

If you need to blur the screen behind the window, there are mainly the following operations:

1. Add FLAG_BLUR_BEHIND to window flags to enable "blur behind screen". Alternatively, set R.attr.windowBlurBehindEnabled in the window theme.

2. Call WindowManager.LayoutParams#setBlurBehindRadius to set the radius of "Blur Behind Screen". Alternatively, set R.attr.windowBlurBehindRadius in the window theme.

3. (Optional) Choose a complementary shade. Window#setDimAmount(float amount)

4. Handle the state of enabling and disabling obfuscation.

3. Rendering

The API for blurring the background of the window and the screen behind the window in Android 12 was briefly introduced above. The following is the final rendering of our implementation. Using Dialog and Activity to achieve blurring only the current background, only the rear screen, and the current background at the same time And the rear screen three commonly used Gaussian blur effects.

1. Use Dialog to achieve Gaussian blur effect
insert image description here
2. Use Activity to achieve Gaussian blur effect
insert image description here

Fourth, the specific code

Finally, let's take a look at the complete project code.

1. gradle configuration file, AndroidManifest.xml manifest file and style.xml style file

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 and activity_main.xml layout files

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 and dialog_background_blur.xml layout files

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 and activity_background_blur.xml layout files

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. Confirm whether the device supports Gaussian blur

If the Gaussian blur function is not enabled on the device, the above code cannot achieve the Gaussian blur effect; to check whether the Android12+ device supports window blur and whether window blur is currently enabled, there are two methods:

1. Use the adb command line:

adb shell wm disable-blur 

1) The device supports it, and the Gaussian blur function switch is turned on

Blur supported on device: true
Blur enabled: true

2) The device does not support

Blur supported on device: false
Blur enabled: false

2. Enter the settings

1) Settings -> System -> Developer Options -> Hardware Accelerated Rendering -> Allow Window Level Blur

2) If you can't find the option under Settings, your device doesn't support window blur.

6. Turn on the Gaussian blur function for the device

1. Turn on the Gaussian blur function by modifying the system compilation file

Because the Gaussian blur function consumes a lot of GPU resources, the Android12 system source code turns off the Gaussian blur function by default when compiling.
insert image description here

device/qcom/msmnile_gvmq/msmnile_gvmq.mk

# enable surface flinger window blurs
PRODUCT_PROPERTY_OVERRIDES += \
       ro.surface_flinger.supports_background_blur=1

2. After rooting the system, directly modify the system source code file

vendor/build.prop

ro.surface_flinger.supports_background_blur=1

Guess you like

Origin blog.csdn.net/abc6368765/article/details/127657069