Desenfoque de ventanas

En Android 12, las APIs públicas están disponibles para Implementar efectos de desenfoque de ventana, como desenfoque del fondo y desenfoque detrás

Los difuminados de ventanas, o entre ventanas, se utilizan para difuminar la pantalla detrás del período determinado. Hay dos tipos de desenfoques de ventanas, que se pueden usar para lograr diferentes efectos visuales:

  • El desenfoque del fondo te permite crear ventanas con fondos desenfocados. creando un efecto de vidrio esmerilado.

  • La opción Desenfocar detrás te permite desenfocar toda la pantalla detrás de una ventana (diálogo). lo que crea un efecto de profundidad de campo.

Los dos efectos se pueden usar por separado o combinar, como se muestra en la siguiente imagen:

solo desenfoque del fondo

a

desenfocar solo detrás

a

desenfoque detrás y desenfoque del fondo

c

Figura 1: Solo desenfoque del fondo (a), desenfoque del fondo (b), desenfoque del fondo y desenfoque detrás (c)

La función de desenfoque de ventanas funciona en todas las ventanas, por lo que también funciona cuando hay otra aplicación detrás de la ventana. Este efecto no es el mismo que efecto de renderización de desenfoque, lo que desenfoca el contenido dentro de la misma ventana. El desenfoque de las ventanas es útil para los diálogos, las hojas inferiores y otras ventanas flotantes.

Implementación

Desarrolladores de apps

Los desarrolladores de apps deben proporcionar un radio de desenfoque para crear un efecto de desenfoque. El radio de desenfoque controla la densidad del desenfoque, es decir, cuanto más alto sea el radio más denso será el desenfoque. Un desenfoque de 0 px significa que no está difuminado. Para desenfocar el fondo, de 20 px crea un buen efecto de campo, mientras que el fondo un radio de desenfoque de 80 px crea un buen efecto de vidrio esmerilado. Evita el desenfoque superior a 150 px, ya que esto afectará significativamente el rendimiento.

Para lograr el efecto de desenfoque deseado y mejorar la legibilidad, elige un desenfoque de radio complementado con una capa de color translúcida.

Desenfoque del fondo

Usa el desenfoque del fondo en ventanas flotantes para crear un efecto de fondo de ventana que es una imagen desenfocada del contenido subyacente. Para agregar un fondo desenfocado a la ventana, haz lo siguiente:

  1. Llama a Window#setBackgroundBlurRadius(int) para establecer un radio de desenfoque del fondo. También puedes establecer R.attr.windowBackgroundBlurRadius en el tema de la ventana.

  2. Configura R.attr.windowIsTranslucent. como true para que la ventana sea translúcida. El desenfoque se dibuja debajo de la ventana por lo que la ventana debe ser translúcida para que el desenfoque sea visible.

  3. De manera opcional, llama a Window#setBackgroundDrawableResource(int) para Agrega un elemento de diseño de fondo de ventana rectangular con un color translúcido. También puedes establecer R.attr.windowBackground en el tema de la ventana.

  4. Para una ventana con esquinas redondeadas, determina las esquinas redondeadas de la área desenfocada estableciendo un ShapeDrawable con esquinas redondeadas como elemento de diseño de fondo de la ventana.

  5. Controla los estados inhabilitados y habilitados para el desenfoque Consulta los Lineamientos para usar el desenfoque de ventanas en apps para obtener más información.

Desenfoque detrás

El desenfoque de la pantalla desenfoca toda la pantalla detrás de la ventana. Este efecto se usa para dirigir la atención del usuario hacia el contenido de la ventana difuminando cualquier elemento en la pantalla detrás de la ventana.

Para desenfocar el contenido detrás de la ventana, sigue estos pasos:

  1. Agregar FLAG_BLUR_BEHIND a las marcas de las ventanas, para habilitar el desenfoque detrás. O, en el tema de la ventana, establece R.attr.windowBlurBehindEnabled.

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

  3. De manera opcional, elige una cantidad de atenuación complementaria.

  4. Controla los estados inhabilitados y habilitados para el desenfoque Consulta los Lineamientos para usar el desenfoque de ventanas en apps para obtener más información.

Lineamientos para usar el desenfoque de ventanas en apps

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

  • Versión de Android: Las APIs de desenfoque de ventanas solo están disponibles en Android 12 y mayores. Consulta el SDK del dispositivo para conocer la versión de Android.

  • Rendimiento de los gráficos: Los dispositivos con GPU de menor rendimiento pueden optar por no hacerlo admite el desenfoque de ventanas.

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

Para que tu app sea compatible con versiones de Android, dispositivos y estados del sistema, sigue estos lineamientos:

  • Agrega un objeto de escucha mediante WindowManager#addCrossWindowBlurEnabledListener. para notificarte cuando el desenfoque de las ventanas esté habilitado o inhabilitado. Además: usa WindowManager#isCrossWindowBlurEnabled para consultar si está habilitada la difuminación de ventanas.

  • Implementa dos versiones para el fondo de la ventana de modo que se ajusten a los elementos habilitados o inhabilitado para el difuminado de ventanas.

    Cuando el desenfoque está habilitado, el fondo de la ventana debe ser traslúcido para que el desenfoque visible. En este estado, cuando se inhabilita el desenfoque, 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 ese efecto, cuando la ventana se desenfoca están inhabilitados, adapta la IU de la app de la siguiente manera:

    • Para desenfocar el fondo, aumenta el valor alfa del fondo de la ventana drawable, lo que lo hace más opaco.

    • Para desenfocar el fondo, agrega una capa atenuada con una mayor intensidad de atenuación.

Ejemplo de desenfoque detrás y desenfoque del fondo

En esta sección, se proporciona un ejemplo práctico de una actividad que utiliza tanto desenfoque desenfoque detrás y del fondo.

El siguiente ejemplo de MainActivity.java es un diálogo con desenfoque detrás de un radio de 20 px y un radio de desenfoque del fondo de 80 px. Tiene esquinas redondeadas, definidas en XML en el elemento de diseño de fondo de la ventana Presenta correctamente controla diferentes versiones de Android y dispositivos (que posiblemente admite difuminado de ventanas) y el desenfoque en tiempo de ejecución habilita o inhabilita los cambios. Garantiza que el contenido del diálogo sea legible en cualquiera de esas condiciones ajustando la cantidad de atenuación de la ventana y del elemento de diseño de fondo de la ventana alfa.

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 en la ventana, definimos el fondo de la ventana en res/drawable/window_background.xml como un 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>

El desenfoque de la ventana desenfoca el contenido de la ventana debajo de la actividad. El la imagen desenfocada se dibuja debajo de esta ventana de actividad, por lo que la ventana de actividad debe ser translúcida para que el desenfoque se vea sean visibles. Para que la ventana sea translúcida, configuramos R.attr.windowIsTranslucent en el tema de la 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 se difuminen las ventanas de un dispositivo, el OEM debe declarar que el dispositivo admite el desenfoque de ventanas.

Para comprobar si tu dispositivo admite el desenfoque de ventanas, haz lo siguiente:

  • Asegúrate de que el dispositivo pueda manejar la carga adicional de la GPU. Dispositivos de gama baja podría no manejar la carga adicional, lo que puede provocar una disminución de los fotogramas. Habilita el desenfoque de las ventanas solo en los dispositivos probados con suficiente potencia de GPU.

  • Si tienes un motor de renderización personalizado, asegúrate de que implementa la lógica de desenfoque. Android 12 predeterminado motor de renderización implementa la lógica de desenfoque en BlurFilter.cpp.

Una vez que te asegures de que tu dispositivo admite el desenfoque de ventanas, establece lo siguiente: flinger de superficie sysprop:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Validación

Cómo validar que la ventana de tu app tenga el control adecuado cuando cambie entre el desenfoque estado habilitado y desenfocado, sigue estos pasos:

  1. Abre la IU que tiene el desenfoque.

  2. Para habilitar o inhabilitar el desenfoque de las ventanas, activa o desactiva el desenfoque de las ventanas.

  3. Verifica que la IU de la ventana cambie hacia y desde el estado de desenfoque como se esperaba.

Cómo activar y desactivar el desenfoque de las ventanas

Para probar cómo se renderiza la IU de la ventana con el efecto de desenfoque de ventana, habilita o inhabilita el desenfoque con uno de los siguientes métodos:

  • En Opciones para desarrolladores:

    Configuración -> Sistema -> Opciones para desarrolladores -> Renderización de hardware acelerada -> Permitir desenfoques a nivel de ventana

  • Desde la terminal en un dispositivo con permisos de administrador:

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

Cómo verificar si tu dispositivo Android 12 y versiones posteriores es compatible con la ventana difuminación de ventanas y si el difuminado de ventanas está actualmente habilitado, ejecuta adb shell wm disable-blur en un dispositivo con permisos de administrador

Solución de problemas

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

Sin difuminar

El dispositivo de prueba no es compatible con el desenfoque de ventanas

  • Prueba tu aplicación en el emulador de Android 12. Para configurar un emulador de Android, consulta Configurar un emulador de Android. Cualquier dispositivo virtual de Android que crees con el emulador es compatible con la ventana. se desenfocan.

Sin esquinas redondeadas

La actualización de la opción para desarrolladores no habilita el desenfoque.

  • Comprueba si el dispositivo está en modo de ahorro de batería o si está en uso tunelización multimedia. En algunos dispositivos de TV, es posible que el desenfoque de las ventanas también se inhabilite durante la reproducción de videos.

El desenfoque del fondo se dibujó en pantalla completa, fuera de los límites de la ventana

Las actualizaciones del objeto de escucha no se aplican a la pantalla.

  • Las actualizaciones del objeto de escucha podrían aplicarse a una instancia de ventana antigua. Comprueba si la ventana se destruye y se vuelve a crear con el elemento actualización del objeto de escucha.