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 desenfoques de ventana o entre ventanas se usan para desenfocar la pantalla detrás de la ventana determinada. Existen dos tipos de desenfoques de ventanas, 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 genera 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 de fondo (a), solo desenfoque detrás (b), desenfoque de fondo 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 app detrás de la 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 ventanas son útiles para diálogos, 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 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 cristal esmerilado. Evita los 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 translúcida.
Desenfoque del fondo
Usa el desenfoque de fondo en las ventanas flotantes para crear un efecto de fondo de ventana, que es una imagen desenfocada del contenido subyacente. Para agregar un fondo desenfocado a tu ventana, haz lo siguiente:
Llama a Window#setBackgroundBlurRadius(int) para establecer un radio de desenfoque de fondo. O bien, en el tema de la ventana, configura R.attr.windowBackgroundBlurRadius.
Establece 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 esta debe ser translúcida para que el desenfoque sea visible.
De manera opcional, llama a Window#setBackgroundDrawableResource(int) para agregar un elemento de diseño de fondo de ventana rectangular con un color translúcido. O bien, en el tema de la ventana, configura R.attr.windowBackground.
En el caso de una ventana con esquinas redondeadas, determina las esquinas redondeadas del área desenfocada configurando un ShapeDrawable con esquinas redondeadas como el elemento de diseño del fondo de la ventana.
Controla los estados habilitados e inhabilitados del desenfoque. Consulta la sección Lineamientos para usar el desenfoque de ventanas en apps para obtener más información.
Desenfoque detrás
El desenfoque detrás 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, ya que se desenfoca todo lo que aparece en la pantalla detrás de la ventana.
Para desenfocar el contenido detrás de la ventana, sigue estos pasos:
Agrega
FLAG_BLUR_BEHINDa las marcas de la ventana para habilitar el desenfoque detrás. O bien, en el tema de la ventana, establece R.attr.windowBlurBehindEnabled.Llama a
WindowManager.LayoutParams#setBlurBehindRadiuspara establecer un radio de desenfoque detrás. O bien, en el tema de la ventana, configura R.attr.windowBlurBehindRadius.De manera opcional, elige un nivel de atenuación complementario.
Controla los estados habilitados e inhabilitados del desenfoque. Consulta la sección Lineamientos para usar el desenfoque de ventanas en apps para obtener más información.
Lineamientos para usar el desenfoque de ventanas en las 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 los gráficos: Es posible que los dispositivos con GPUs de menor rendimiento decidan no admitir el desenfoque de ventanas.
Estado del sistema: El servidor del sistema puede inhabilitar temporalmente el desenfoque de ventanas durante el 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 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 recibir una notificación cuando se habiliten o inhabiliten los desenfoques de ventana. Además, usa
WindowManager#isCrossWindowBlurEnabledpara consultar si los desenfoques de ventanas están habilitados actualmente.Implementa dos versiones para el fondo de la ventana, de modo que se adapte al estado habilitado o inhabilitado de los desenfoques de ventana.
Cuando se habilitan los desenfoques, el fondo de la ventana debe ser traslúcido para que el desenfoque sea visible. 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 inhabiliten 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 del fondo de la ventana, lo que lo hará más opaco.
Para desenfocar el fondo, agrega una capa de atenuación con un valor de atenuación más alto.
Ejemplo de desenfoque detrás y desenfoque de fondo
En esta sección, se proporciona un ejemplo de trabajo de una actividad que usa el desenfoque detrás y el desenfoque de fondo.
En el siguiente ejemplo de MainActivity.java, se muestra 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. Controla correctamente las diferentes versiones de Android, los diferentes dispositivos (que posiblemente no admitan el desenfoque de ventanas) y los cambios de desenfoque de tiempo de ejecución habilitados o inhabilitados. Para garantizar que el contenido del diálogo sea legible en cualquiera de esas condiciones, se ajusta el alfa del elemento de diseño 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 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 desenfocada se dibuja debajo de esta ventana de actividad, por lo que la ventana de actividad debe ser translúcida para que el desenfoque sea visible. 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>
OEMs y socios
Para que un dispositivo tenga desenfoque de ventanas, el OEM debe declarar que el dispositivo admite el desenfoque 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. Habilita el desenfoque de ventanas solo 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 el desenfoque 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 tenga el control adecuado cuando se cambia entre los estados de desenfoque habilitado y desenfoque inhabilitado, sigue estos pasos:
Abre la IU que tiene desenfoque.
Habilita o inhabilita el desenfoque de ventanas activando y desactivando el desenfoque de ventanas.
Verifica que la IU de la ventana cambie hacia y desde un estado desenfocado como se espera.
Cómo activar y desactivar el desenfoque de ventanas
Para probar cómo se renderiza la IU de la ventana con el efecto de desenfoque, habilita o inhabilita los desenfoques con uno de los siguientes métodos:
En Opciones para desarrolladores:
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:
adb shell wm disable-blur 1 # 1 disables window blurs, 0 allows them
Para verificar si tu dispositivo con Android 12 o una versión posterior admite el desenfoque de ventanas y si está habilitado, ejecuta adb shell wm disable-blur en un dispositivo con permisos de administrador.
Solución de problemas
Usa la siguiente 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 ventanas.
Asegúrate de establecer un color de fondo de ventana translúcido. Un color de fondo de ventana opaco oculta el área desenfocada.
El dispositivo de prueba no admite el desenfoque de ventanas.
- Prueba tu aplicación en el emulador de Android 12. Para configurar un Android Emulator, consulta Cómo configurar un Android Emulator. 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 las esquinas redondeadas. Este elemento de diseño determina el contorno del área desenfocada.
Actualizar la opción para desarrolladores no habilita los desenfoques
- Verifica si el dispositivo está en modo de ahorro de batería o si usa la tunelización multimedia. En algunos dispositivos de TV, es posible que también se inhabilite el desenfoque 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 objeto de escucha 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. Verifica si la ventana se destruye y se vuelve a crear con la actualización correcta del objeto de escucha.
