Desenfoques de ventana

En Android 12, hay API públicas disponibles para implementar efectos de desenfoque de ventana, como desenfoque de fondo y desenfoque detrás.

Los desenfoques de ventana, o desenfoques entre ventanas, se utilizan para desenfocar la pantalla detrás de la ventana determinada. Hay dos tipos de desenfoques de ventana, que se pueden utilizar para lograr diferentes efectos visuales:

  • El desenfoque de fondo le permite crear ventanas con fondos borrosos, creando un efecto de vidrio esmerilado.

  • Desenfocar detrás le permite desenfocar toda la pantalla detrás de una ventana (de diálogo), creando un efecto de profundidad de campo.

Los dos efectos se pueden utilizar por separado o combinados, como se muestra en la siguiente figura:

fondo borroso solamente

a

desenfocar solo detrás

b

desenfoque detrás y desenfoque de fondo

C

Figura 1. Sólo desenfoque de fondo (a), sólo desenfoque detrás (b), fondo desenfocado y desenfoque detrás (c)

La función de desenfoque de ventana funciona en todas las ventanas, lo que significa que también funciona cuando hay otra aplicación detrás de la ventana. Este efecto no es lo mismo que el efecto de renderizado de desenfoque , que desenfoca el contenido dentro de la misma ventana. Los desenfoques de ventanas son útiles para cuadros de diálogo, hojas inferiores y otras ventanas flotantes.

Implementación

Desarrolladores de aplicaciones

Los desarrolladores de aplicaciones deben proporcionar un radio de desenfoque para crear un efecto de desenfoque. El radio de desenfoque controla qué tan denso es el desenfoque, es decir, cuanto mayor sea el radio, más denso será el desenfoque. Un desenfoque de 0 px significa que no hay desenfoque. Para el desenfoque detrás, un radio de 20 px crea un buen efecto de profundidad de campo, mientras que un radio de desenfoque de fondo de 80 px crea un buen efecto de vidrio esmerilado. Evite radios de desenfoque superiores a 150 px, ya que esto afectará significativamente el rendimiento.

Para lograr el efecto de desenfoque deseado y aumentar la legibilidad, elija un valor de radio de desenfoque complementado con una capa de color translúcido.

Desenfoque de fondo

Utilice el desenfoque de fondo en ventanas flotantes para crear un efecto de fondo de ventana que sea una imagen borrosa del contenido subyacente. Para agregar un fondo borroso a su ventana, haga lo siguiente:

  1. Llame a Window#setBackgroundBlurRadius(int) para establecer un radio de desenfoque de fondo. O, en el tema de la ventana, configure R.attr.windowBackgroundBlurRadius .

  2. Establezca R.attr.windowIsTranslucent en verdadero para que la ventana sea translúcida. El desenfoque se dibuja debajo de la superficie de la ventana, por lo que la ventana debe ser translúcida para que el desenfoque sea visible.

  3. Opcionalmente, llame a Window#setBackgroundDrawableResource(int) para agregar un fondo de ventana rectangular dibujable con un color translúcido. O, en el tema de la ventana, configure R.attr.windowBackground .

  4. Para una ventana con esquinas redondeadas, determine las esquinas redondeadas para el área borrosa configurando un ShapeDrawable con esquinas redondeadas como el fondo de la ventana dibujable.

  5. Manejar los estados de desenfoque habilitado y deshabilitado. Consulte la sección Pautas para usar el desenfoque de ventana en aplicaciones para obtener más información.

Desenfoque detrás

El desenfoque detrás desdibuja toda la pantalla detrás de la ventana. Este efecto se utiliza para dirigir la atención del usuario hacia el contenido de la ventana desenfocando cualquier cosa en la pantalla detrás de la ventana.

Para difuminar el contenido detrás de su ventana, siga estos pasos:

  1. Agregue FLAG_BLUR_BEHIND a las banderas de la ventana para permitir el desenfoque detrás. O, en el tema de la ventana, configure R.attr.windowBlurBehindEnabled .

  2. Llame a WindowManager.LayoutParams#setBlurBehindRadius para establecer un desenfoque detrás del radio. O, en el tema de la ventana, configure R.attr.windowBlurBehindRadius .

  3. Opcionalmente, elija una cantidad tenue complementaria.

  4. Manejar los estados de desenfoque habilitado y deshabilitado. Consulte la sección Pautas para usar el desenfoque de ventana en aplicaciones para obtener más información.

Pautas para usar el desenfoque de ventana en aplicaciones

La compatibilidad con el desenfoque de Windows depende de lo siguiente:

  • Versión de Android: las API de Windows Blur están disponibles solo en Android 12 y versiones posteriores. Consulte el SDK del dispositivo para conocer la versión de Android.

  • Rendimiento de gráficos: los dispositivos con GPU de menor rendimiento pueden optar por no admitir ventanas borrosas.

  • Estado del sistema: el servidor del sistema puede desactivar temporalmente las ventanas borrosas en tiempo de ejecución, por ejemplo, durante el modo de ahorro de batería, mientras se reproducen ciertos tipos de contenido de video o debido a una anulación del desarrollador.

Para que su aplicación sea compatible con todas las versiones, dispositivos y estados del sistema de Android, siga estas pautas:

  • Agregue un oyente a través de WindowManager#addCrossWindowBlurEnabledListener , para notificarle cuando los desenfoques de ventana estén habilitados o deshabilitados. Además, utilice WindowManager#isCrossWindowBlurEnabled para consultar si los desenfoques de ventanas están habilitados actualmente.

  • Implemente dos versiones para el fondo de la ventana, para adaptarse al estado habilitado o deshabilitado de los desenfoques de la ventana.

    Cuando los desenfoques están habilitados, el fondo de la ventana debe ser translúcido para que el desenfoque sea visible. En este estado, cuando se desactivan los desenfoques, el contenido de la ventana se superpone directamente con el contenido de la ventana subyacente, lo que hace que la ventana superpuesta sea menos legible. Para evitar este efecto, cuando las ventanas desenfocadas estén desactivadas, adapte la interfaz de usuario de la aplicación de la siguiente manera:

    • Para desenfocar el fondo, aumente el alfa del fondo dibujable de la ventana, haciéndolo más opaco.

    • Para desenfocar detrás, agregue una capa tenue con una cantidad de tenue mayor.

Ejemplo de desenfoque detrás y desenfoque de fondo

Esta sección proporciona un ejemplo práctico de una actividad que utiliza tanto el desenfoque detrás como el desenfoque del fondo.

El siguiente ejemplo de MainActivity.java es un cuadro de diálogo con un radio de desenfoque detrás de 20 px y un radio de desenfoque de fondo de 80 px. Tiene esquinas redondeadas, definidas en xml en el dibujable de fondo de la ventana. Maneja correctamente diferentes versiones de Android, diferentes dispositivos (que potencialmente no admiten ventanas desenfocadas) y cambios habilitados o deshabilitados en tiempo de ejecución. Garantiza que el contenido del cuadro de diálogo sea legible en cualquiera de esas condiciones ajustando el alfa dibujable del fondo de la ventana y la cantidad de atenuación de la ventana.

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;
    }
}

Para crear esquinas redondeadas para la ventana, definimos el fondo de la ventana en res/drawable/window_background.xml como ShapeDrawable con esquinas redondeadas con un radio de 20 dp de la siguiente manera:

<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle" >
    <corners android:radius="20dp"/>
    <solid android:color="#AAAAAA"/>
</shape>

Los desenfoques de ventana desenfocan el contenido de la ventana debajo de la actividad. La imagen borrosa se dibuja debajo de esta ventana de actividad, por lo que la ventana de actividad debe ser translúcida para permitir que el desenfoque sea visible. Para hacer que la ventana sea translúcida configuramos R.attr.windowIsTranslucent en el tema de actividad de la siguiente manera:

<style name="Theme.BlurryDialog" parent="Theme.MaterialComponents.Dialog">
    <item name="android:windowIsTranslucent">true</item>
</style>

OEM y socios

Para que las ventanas se vean borrosas en un dispositivo, el OEM debe declarar que el dispositivo admite ventanas borrosas.

Para comprobar si su dispositivo admite ventanas desenfocadas, haga lo siguiente:

  • Asegúrese de que el dispositivo pueda soportar la carga adicional de GPU. Es posible que los dispositivos de gama baja no puedan soportar la carga adicional, lo que puede provocar la caída de fotogramas. Habilite las ventanas borrosas solo en dispositivos probados con suficiente potencia de GPU.

  • Si tiene un motor de renderizado personalizado, asegúrese de que su motor de renderizado implemente la lógica de desenfoque. El motor de renderizado predeterminado de Android 12 implementa la lógica de desenfoque en BlurFilter.cpp .

Una vez que se asegure de que su dispositivo admite ventanas desenfocadas, configure el siguiente sysprop de deflector de superficie:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Validación

Para validar que la ventana de su aplicación tenga un manejo adecuado al cambiar entre los estados de desenfoque habilitado y desenfocado deshabilitado, siga estos pasos:

  1. Abra la interfaz de usuario que está borrosa.

  2. Habilite o deshabilite el desenfoque de ventana activando y desactivando el desenfoque de ventana .

  3. Verifique que la interfaz de usuario de la ventana cambie hacia y desde un estado borroso como se esperaba.

Activar y desactivar el desenfoque de ventana

Para probar cómo se representa la interfaz de usuario de la ventana con el efecto de desenfoque de ventana, habilite o deshabilite los desenfoques utilizando uno de los siguientes métodos:

  • Desde Opciones de desarrollador:

    Configuración -> Sistema -> Opciones de desarrollador -> Representación acelerada por hardware -> Permitir desenfoques a nivel de ventana

  • Desde la terminal en un dispositivo rooteado:

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

Para verificar si su dispositivo Android 12+ admite ventanas desenfocadas y si las ventanas desenfocadas están actualmente habilitadas, ejecute adb shell wm disable-blur en un dispositivo rooteado.

Solución de problemas

Utilice lo siguiente como guía para solucionar problemas durante la validación.

No hay desenfoque dibujado

  • Verifique que los desenfoques estén actualmente habilitados y que su hardware los admita. Consulte Activar y desactivar el desenfoque de ventana .

  • Asegúrese de establecer un color de fondo de ventana translúcido. Un color de fondo de ventana opaco oculta el área borrosa.

El dispositivo de prueba no admite ventanas borrosas

  • Pruebe su aplicación en el emulador de Android 12. Para configurar un emulador de Android, consulte Configurar un emulador de Android . Cualquier dispositivo virtual Android que cree con el emulador admite ventanas borrosas.

Sin esquinas redondeadas

La actualización de la opción de desarrollador no habilita los desenfoques

  • Compruebe si el dispositivo está en modo de ahorro de batería o si está utilizando un túnel multimedia . En algunos dispositivos de TV, las ventanas borrosas también pueden desactivarse durante la reproducción de video.

Fondo borroso dibujado en pantalla completa, fuera de los límites de la ventana

Las actualizaciones del oyente no se aplican en la pantalla.

  • Es posible que las actualizaciones del detector se estén aplicando a una instancia de ventana antigua. Compruebe si la ventana se destruye y se vuelve a crear con la actualización del oyente adecuada.