En Android 12, hay APIs públicas disponibles para implementar efectos de desenfoque de ventana, como desenfoque del fondo y desenfoque detrás.
Los desenfoques de ventana, o desenfoques entre ventanas, se usan para desenfocar la pantalla detrás de la ventana determinada. Hay dos tipos de desenfoque de ventana que se pueden usar para lograr diferentes efectos visuales:
El desenfoque de fondo te permite crear ventanas con fondos desenfocados, lo que genera un efecto de cristal esmerilado.
Desenfoque detrás te permite desenfocar toda la pantalla detrás de una ventana (de diálogo), lo que crea un efecto de profundidad de campo.
Los dos efectos se pueden usar por separado o combinados, como se muestra en la siguiente figura:
![]() a |
![]() b |
![]() c |
Figura 1: Solo desenfoque del fondo (a), solo desenfoque detrás (b), desenfoque del fondo y desenfoque detrás (c)
La función de desenfoque de ventanas funciona en todas las ventanas, lo que significa que también funciona cuando hay otra app detrás de tu ventana. Este efecto no es el mismo que el efecto de renderización de desenfoque, que desenfoca el contenido dentro de la misma ventana. Los desenfoques de ventana son útiles para los diálogos, las hojas inferiores y otras ventanas flotantes.
Implementación
Desarrolladores de apps
Los desarrolladores de aplicaciones deben proporcionar un radio de desenfoque para crear un efecto de desenfoque. El radio de desenfoque controla la densidad del desenfoque, es decir, cuanto mayor sea el radio, más denso será el desenfoque. Un desenfoque de 0 px significa que no hay desenfoque. En el caso del 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 cristal esmerilado. Evita usar 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, elige un valor de radio de desenfoque complementado con una capa de color traslúcida.
Desenfoque del fondo
Usa el desenfoque de fondo en ventanas flotantes para crear un efecto de fondo de ventana que sea una imagen desenfocada del contenido subyacente. Para agregar un fondo desenfocado a la ventana, haz lo siguiente:
Llama a Window#setBackgroundBlurRadius(int) para establecer un radio de desenfoque del fondo. O bien, en el tema de la ventana, configura R.attr.windowBackgroundBlurRadius.
Establece R.attr.windowIsTranslucent en verdadero para que la ventana sea traslúcida. El desenfoque se dibuja debajo de la superficie de la ventana, por lo que esta debe ser translúcida para que se vea el desenfoque.
De manera opcional, llama a Window#setBackgroundDrawableResource(int) para agregar un elemento de diseño rectangular de fondo de ventana con un color translúcido. O bien, en el tema de la ventana, configura R.attr.windowBackground.
Para una ventana con esquinas redondeadas, determina las esquinas redondeadas del área desenfocada configurando un ShapeDrawable con esquinas redondeadas como el elemento de diseño en segundo plano de la ventana.
Controla los estados habilitado e inhabilitado del desenfoque. Consulta la sección Lineamientos para usar el desenfoque de ventana en apps para obtener más información.
Desenfoque detrás
El desenfoque detrás de la ventana desenfoca toda la pantalla. Este efecto se usa para dirigir la atención del usuario hacia el contenido de la ventana desenfocando todo lo que se encuentra en la pantalla detrás de la ventana.
Para desenfocar el contenido detrás de la ventana, sigue estos pasos:
Agrega
FLAG_BLUR_BEHIND
a las marcas de la ventana para habilitar el desenfoque en segundo plano. O bien, en el tema de la ventana, configura R.attr.windowBlurBehindEnabled.Llama a
WindowManager.LayoutParams#setBlurBehindRadius
para establecer un radio detrás del desenfoque. O bien, en el tema de la ventana, configura R.attr.windowBlurBehindRadius.De manera opcional, elige un importe de atenuación complementario.
Controla los estados habilitado e inhabilitado del desenfoque. Consulta la sección Lineamientos para usar el desenfoque de ventana 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 versiones posteriores. Verifica el SDK del dispositivo para la versión de Android.
Rendimiento de gráficos: Es posible que los dispositivos con GPU de menor rendimiento no admitan el desenfoque de ventanas.
Estado del sistema: El servidor del sistema puede inhabilitar temporalmente los desenfoques de ventanas en el tiempo de ejecución, por ejemplo, durante el modo de ahorro de batería, mientras se reproduce cierto tipo de contenido de video o debido a una anulación del desarrollador.
Para que tu app sea compatible con todas las versiones de Android, los dispositivos y los estados del sistema, sigue estos lineamientos:
Agrega un objeto de escucha a través de WindowManager#addCrossWindowBlurEnabledListener para que se te notifique cuando se habiliten o inhabiliten los desenfoques de ventanas. Además, usa
WindowManager#isCrossWindowBlurEnabled
para consultar si los desenfoques de ventana están habilitados actualmente.Implementa dos versiones para el fondo de la ventana, para adaptarse al estado habilitado o inhabilitado de los desenfoques de la ventana.
Cuando se habilitan los desenfoques, el fondo de la ventana debe ser translúcido para que se vea el desenfoque. En este estado, cuando se inhabilitan 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 se inhabilitan los desenfoques de ventana, adapta la IU de la app de la siguiente manera:
Para el desenfoque de fondo, aumenta el valor alfa del elemento de diseño de fondo de la ventana, lo que lo hace más opaco.
Para el desenfoque detrás, agrega una capa tenue con una cantidad de atenuación mayor.
Ejemplo de desenfoque detrás y desenfoque de fondo
En esta sección, se proporciona un ejemplo práctico de una actividad que usa el desenfoque detrás y el desenfoque de fondo.
El siguiente ejemplo de MainActivity.java
es un 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 elemento de diseño del fondo de la ventana. Maneja correctamente diferentes versiones de Android, diferentes dispositivos (que posiblemente no admitan desenfoques de ventana) y cambios de tiempo de ejecución de desenfoque habilitado o inhabilitado. Garantiza que el contenido del diálogo sea legible en cualquiera de esas condiciones ajustando el valor alfa del elemento de diseño de 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 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>
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 que se vea el desenfoque. Para que la ventana sea traslú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>
OEMs y socios
Para que un dispositivo tenga desenfoque de ventanas, el OEM debe declarar que el dispositivo admite desenfoques de ventanas.
Para verificar si tu dispositivo admite el desenfoque de ventanas, haz lo siguiente:
Asegúrate de que el dispositivo pueda controlar la carga adicional de la GPU. Es posible que los dispositivos de gama baja no puedan controlar la carga adicional, lo que puede provocar la pérdida de fotogramas. Solo habilita el desenfoque de ventanas en dispositivos probados con suficiente potencia de GPU.
Si tienes un motor de renderización personalizado, asegúrate de que implemente la lógica de desenfoque. El motor de renderización predeterminado de Android 12 implementa la lógica de desenfoque en
BlurFilter.cpp
.
Una vez que te asegures de que tu dispositivo admite desenfoques de ventanas, configura el siguiente sysprop
de SurfaceFlinger:
PRODUCT_VENDOR_PROPERTIES += \
ro.surface_flinger.supports_background_blur=1
Validación
Para validar que la ventana de tu app se controla correctamente cuando se cambia entre los estados de desenfoque habilitado y desenfoque inhabilitado, sigue estos pasos:
Abre la IU que tiene el desenfoque.
Habilita o inhabilita el desenfoque de ventanas activando y desactivando el desenfoque de ventanas.
Verifica que la IU de la ventana cambie a un estado borroso y desde él según lo esperado.
Cómo activar y desactivar el desenfoque de la ventana
Para probar cómo se renderiza la IU de la ventana con el efecto de desenfoque de la ventana, habilita o inhabilita los desenfoques con uno de los siguientes métodos:
Desde Opciones para desarrolladores, haz lo siguiente:
Configuración -> Sistema -> Opciones para desarrolladores -> Renderización acelerada por hardware -> Permitir desenfoques a nivel de la ventana
Desde la terminal en un dispositivo con permisos de administrador, haz lo siguiente:
adb shell wm disable-blur 1 # 1 disables window blurs, 0 allows them
Para verificar si tu dispositivo con Android 12 o versiones posteriores admite desenfoques de ventanas y si estos están habilitados actualmente, ejecuta adb shell wm disable-blur
en un dispositivo con acceso de administrador.
Solución de problemas
Usa la siguiente información como guía para solucionar problemas durante la validación.
No se dibujó ningún desenfoque.
Verifica que los desenfoques estén habilitados y que tu hardware los admita. Consulta Cómo activar y desactivar el desenfoque de la ventana.
Asegúrate de establecer un color de fondo de ventana traslúcido. Un color de fondo de ventana opaco oculta el área desenfocada.
El dispositivo de prueba no admite desenfoques de ventana
- Prueba tu aplicación en el emulador de Android 12. Para configurar un emulador de Android, consulta Configura un emulador de Android. Cualquier dispositivo virtual de Android que crees con el emulador admite el desenfoque de ventanas.
Sin esquinas redondeadas
- Establece un elemento de diseño de fondo de ventana para definir esquinas redondeadas. Este elemento de diseño determina el contorno del área desenfocada.
La actualización de la opción para desarrolladores no habilita los desenfoques
- Comprueba si el dispositivo está en modo de ahorro de batería o si usa túneles multimedia. En algunos dispositivos de TV, es posible que también se inhabiliten los desenfoques de ventanas durante la reproducción de videos.
El desenfoque del fondo se dibuja en pantalla completa, no dentro de los límites de la ventana.
Verifica android:windowIsFloating para asegurarte de que tu ventana esté marcada como flotante.
Asegúrate de que se haya establecido un elemento de diseño de fondo de ventana. Este parámetro de configuración determina el contorno del área de desenfoque.
Las actualizaciones del usuario no se aplican en la pantalla.
- Es posible que las actualizaciones del objeto de escucha se estén aplicando a una instancia de ventana anterior. Comprueba si la ventana se destruye y se vuelve a crear con la actualización del objeto de escucha correcta.