Размытие окна

В Android 12 доступны общедоступные API для реализации эффектов размытия окон, таких как размытие фона и размытие позади.

Размытие окна, или перекрестное размытие окна, используется для размытия экрана за заданным окном. Существует два типа размытия окна, которые можно использовать для достижения различных визуальных эффектов:

  • Функция размытия фона позволяет создавать окна с размытым фоном, имитируя эффект матового стекла.

  • Функция «Размытие сзади» позволяет размыть весь экран за окном (диалоговым окном), создавая эффект глубины резкости.

Эти два эффекта можно использовать по отдельности или в сочетании, как показано на следующем рисунке:

размытие только фона

а

размытие только сзади

б

размытие сзади и размытие фона

с

Рисунок 1. Размытие только фона (a), размытие только сзади (b), размытие фона и размытие сзади (c)

Функция размытия окна работает между окнами, то есть она работает, даже если за вашим окном находится другое приложение. Этот эффект отличается от эффекта размытия при рендеринге , который размывает содержимое внутри того же окна. Размытие окна полезно для диалоговых окон, всплывающих подсказок и других плавающих окон.

Выполнение

разработчики приложений

Разработчики приложений должны указать радиус размытия для создания эффекта размытия. Радиус размытия определяет плотность размытия, то есть чем больше радиус, тем плотнее размытие. Размытие 0 пикселей означает отсутствие размытия. Для размытия фона радиус 20 пикселей создает хороший эффект глубины резкости, а радиус размытия фона 80 пикселей создает хороший эффект матового стекла. Избегайте радиусов размытия больше 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. При желании выберите подходящее количество затемнения .

  4. Обработка состояний включения и выключения размытия. Дополнительную информацию см. в разделе «Рекомендации по использованию размытия окна в приложениях» .

Рекомендации по использованию эффекта размытия окна в приложениях

Поддержка эффекта размытия фона окна зависит от следующих факторов:

  • Версия Android: API для размытия фона в окнах доступны только в Android 12 и выше. Проверьте SDK устройства, чтобы узнать версию Android.

  • Производительность графики: Устройства с менее производительными графическими процессорами могут не поддерживать размытие окон.

  • Состояние системы: Сервер системы может временно отключать размытие окон во время выполнения, например, в режиме энергосбережения, при воспроизведении определенных типов видеоконтента или по решению разработчика.

Чтобы ваше приложение было совместимо с различными версиями Android, устройствами и состояниями системы, следуйте этим рекомендациям:

  • Добавьте слушатель с помощью метода `WindowManager#addCrossWindowBlurEnabledListener` , чтобы получать уведомления о включении или выключении размытия окна. Кроме того, используйте WindowManager#isCrossWindowBlurEnabled чтобы узнать, включено ли в данный момент размытие окна.

  • Реализуйте два варианта фона окна, чтобы учитывать включенное или выключенное размытие окна.

    Когда размытие включено, фон окна должен быть полупрозрачным, чтобы размытие было видно. В этом состоянии, когда размытие отключено, содержимое окна напрямую перекрывает содержимое нижележащего окна, что делает перекрывающееся окно менее читаемым. Чтобы избежать такого эффекта, при отключении размытия окна адаптируйте пользовательский интерфейс приложения следующим образом:

    • Для размытия фона увеличьте значение прозрачности (alpha) фонового изображения окна, сделав его более непрозрачным.

    • Для создания эффекта размытия сзади добавьте затемненный слой с более высоким уровнем затемнения.

Пример размытия заднего плана и размытия фона.

В этом разделе представлен рабочий пример действия, в котором используется как размытие заднего плана, так и размытие фона.

В приведенном ниже примере файла MainActivity.java представлено диалоговое окно с радиусом размытия фона 20 пикселей и радиусом размытия заднего плана 80 пикселей. Оно имеет закругленные углы, определенные в XML-файле в объекте windowbackground drawable. Оно корректно обрабатывает различные версии Android, различные устройства (которые потенциально не поддерживают размытие окна) и изменения включения или отключения размытия во время выполнения. Оно гарантирует читаемость содержимого диалогового окна при любых из этих условий путем регулировки альфа-канала объекта windowbackground drawable и величины затемнения окна.

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>

Производители оригинального оборудования и партнеры

Для того чтобы на устройстве отображалось размытие окна, производитель должен заявить, что устройство поддерживает эту функцию.

Чтобы проверить, поддерживает ли ваше устройство размытие окон, выполните следующие действия:

  • Убедитесь, что устройство способно справиться с дополнительной нагрузкой на графический процессор. Устройства более низкого класса могут не справиться с дополнительной нагрузкой, что может привести к выпадению кадров. Включайте размытие окон только на протестированных устройствах с достаточной мощностью графического процессора.

  • Если вы используете собственный механизм рендеринга, убедитесь, что он реализует логику размытия. В стандартном механизме рендеринга Android 12 эта логика реализована в BlurFilter.cpp .

Убедившись, что ваше устройство поддерживает размытие окон, установите следующие параметры sysprop для параметра surface flenger:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Проверка

Чтобы убедиться, что окно вашего приложения корректно обрабатывает переключение между состояниями с включенным и выключенным размытием, выполните следующие действия:

  1. Откройте интерфейс, в котором присутствует эффект размытия.

  2. Включить или выключить размытие окна можно, переключив соответствующую кнопку .

  3. Убедитесь, что интерфейс окна переходит в размытое состояние и обратно, как и ожидалось.

Включение и выключение размытия окна

Чтобы проверить, как отображается пользовательский интерфейс окна с эффектом размытия, включите или отключите размытие одним из следующих способов:

  • В разделе «Параметры разработчика»:

    Настройки -> Система -> Параметры разработчика -> Аппаратное ускорение рендеринга -> Разрешить размытие на уровне окна

  • С терминала на устройстве с правами root:

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

Чтобы проверить, поддерживает ли ваше устройство Android 12+ размытие окон и включено ли оно в данный момент, выполните команду adb shell wm disable-blur на устройстве с root-правами.

Поиск неисправностей

Следуйте приведенным ниже инструкциям для устранения неполадок в процессе проверки.

Размытие отсутствует.

  • Убедитесь, что размытие включено и ваше оборудование его поддерживает. См. раздел «Включение и выключение размытия окна» .

  • Убедитесь, что вы установили полупрозрачный цвет фона окна. Непрозрачный цвет фона окна скрывает размытую область.

Тестируемое устройство не поддерживает размытие окон.

  • Протестируйте свое приложение на эмуляторе Android 12. Чтобы настроить эмулятор Android, см. раздел «Настройка эмулятора Android» . Любое виртуальное устройство Android, созданное с помощью эмулятора, поддерживает размытие окон.

Без закругленных углов

Обновление параметров разработчика не включает эффект размытия.

  • Проверьте, находится ли устройство в режиме энергосбережения или использует ли оно мультимедийное туннелирование . На некоторых телевизорах также может быть отключено размытие окон во время воспроизведения видео.

Размытие фона отображается во всем экране, за пределами оконных границ.

Обновления от слушателя не отображаются на экране.

  • Возможно, обновления обработчика событий применяются к старому экземпляру окна. Проверьте, уничтожается ли окно и создается ли оно заново с правильным обновлением обработчика событий.