Desfoques de janela

No Android 12, as APIs públicas estão disponíveis para implementar efeitos de desfoque de janela, como o de fundo e o de fundo.

Desfoques de janela, ou desfoques de janelas, são usados para desfocar a tela atrás do em uma determinada janela. Há dois tipos de desfoques de janela, que podem ser usados para conseguir diferentes efeitos visuais:

  • O desfoque do plano de fundo permite criar janelas com planos de fundo desfocados, criando um efeito de vidro fosco.

  • Desfocar atrás permite desfocar a tela inteira atrás de uma janela (caixa de diálogo). criando um efeito de profundidade de campo.

Os dois efeitos podem ser usados separadamente ou combinados, como mostra a figura a seguir:

somente desfoque do plano de fundo

a

desfocar apenas atrás

b

desfocar atrás e desfocar o plano de fundo

C

Figura 1. Somente desfoque do plano de fundo (a), desfoque apenas atrás (b), desfoque do plano de fundo e desfoque atrás de (c)

O recurso de desfoque de janela funciona em várias janelas, ou seja, também funciona quando há outro aplicativo atrás da sua janela. Este efeito não é igual ao efeito de renderização de desfoque, que desfoca o conteúdo dentro da mesma janela. Desfoques de janela são útil para caixas de diálogo, páginas inferiores e outras janelas flutuantes.

Implementação

Desenvolvedores de apps

Os desenvolvedores de apps precisam fornecer um raio de desfoque para criar um efeito de desfoque. O raio de desfoque controla a densidade do desfoque, ou seja, quanto maior o raio, mais denso será o desfoque. Um desfoque de 0 px significa que não há desfoque. Para desfocar atrás, um um raio de 20 px cria uma boa profundidade de efeito de campo, o raio de desfoque de 80 px cria um bom efeito de vidro fosco. Evitar desfoque raios maiores que 150 px, já que isso afetará significativamente o desempenho.

Escolha um desfoque para alcançar o efeito de desfoque desejado e melhorar a legibilidade. valor de raio complementado com uma camada de cor translúcida.

Desfoque do plano de fundo

Use o desfoque do plano de fundo em janelas flutuantes para criar um efeito de plano de fundo de janela que é uma imagem desfocada do conteúdo subjacente. Para desfocar o plano de fundo da janela, faça o seguinte:

  1. Chame Window#setBackgroundBlurRadius(int) para definir um raio de desfoque do plano de fundo. Ou, no tema da janela, defina R.attr.windowBackgroundBlurRadius.

  2. Defina R.attr.windowIsTranslucent. como verdadeiro para deixar a janela translúcida. O desfoque aparece embaixo da janela de modo que a janela precisa ser translúcida para permitir que o desfoque seja visível.

  3. Opcionalmente, chame Window#setBackgroundDrawableResource(int) para adiciona um drawable de segundo plano de janela retangular com uma cor translúcida. Ou, no tema da janela, defina R.attr.windowBackground.

  4. Para uma janela com cantos arredondados, determine os cantos arredondados para o área desfocada definindo um ShapeDrawable com cantos arredondados como o desenhável do plano de fundo da janela.

  5. Lidar com os estados ativado e desativado de desfoque. Consulte as Diretrizes para usar o desfoque de janela em apps. para mais informações.

Desfoque atrás

O desfoque atrás da janela desfoca toda a tela. Este efeito é usado direcionar a atenção do usuário para o conteúdo da janela desfocando qualquer coisa na tela atrás da janela.

Para desfocar o conteúdo atrás da janela, siga estas etapas:

  1. Adicionar FLAG_BLUR_BEHIND às sinalizações de janela para permitir o desfoque. Ou, no tema da janela, defina R.attr.windowBlurBehindEnabled (link em inglês).

  2. Chame WindowManager.LayoutParams#setBlurBehindRadius para definir um desfoque por trás do raio. Ou, no tema da janela, defina R.attr.windowBlurBehindReach (link em inglês)

  3. Como opção, escolha uma quantidade esmaecida complementar.

  4. Lidar com os estados ativado e desativado de desfoque. Consulte as Diretrizes para usar o desfoque de janela em apps. para mais informações.

Diretrizes para usar o desfoque de janela em apps

O suporte ao desfoque de janelas depende do seguinte:

  • Versão do Android: as APIs de desfoque de janelas estão disponíveis apenas no Android 12 e mais alto. Confira a versão do Android no SDK do dispositivo.

  • Desempenho gráfico: dispositivos com GPUs de menor desempenho podem optar por não fazer isso oferecer suporte a desfoques de janela.

  • Estado do sistema: o servidor do sistema pode desativar temporariamente os desfoques de janela em de tempo de execução, por exemplo, durante o modo de economia de bateria, enquanto reproduz alguns tipos de conteúdo de vídeo ou devido a uma substituição pelo desenvolvedor.

Para tornar seu app compatível com as versões do Android, os dispositivos e os estados do sistema, siga estas diretrizes:

  • Adicione um listener por meio de WindowManager#addCrossWindowBlurEnabledListener. para notificar você quando os desfoques de janela estiverem ativados ou desativados. Além disso, use WindowManager#isCrossWindowBlurEnabled para consultar se os desfoques de janela estão ativados.

  • Implemente duas versões para o segundo plano da janela de modo a acomodar o ou desativado de desfoques de janela.

    Quando os desfoques estão ativados, o plano de fundo da janela precisa ser translúcido para deixar o desfoque visível. Nesse estado, quando os desfoques são desativados, o conteúdo da janela se sobrepõe diretamente ao conteúdo da janela subjacente, tornando a sobreposta, tornando menos legível. Para evitar esse efeito, quando a janela é desfocada estiverem desativados, adapte a interface do app da seguinte maneira:

    • Para desfocar o plano de fundo, aumente o Alfa do plano de fundo da janela drawable, tornando-o mais opaco.

    • Para desfocar atrás, adicione uma camada escura com uma quantidade de escurecimento mais alta.

Exemplo de desfoque atrás e no plano de fundo

Esta seção fornece um exemplo funcional de uma atividade que usa o desfoque e desfoque do plano de fundo.

O exemplo abaixo de MainActivity.java é uma caixa de diálogo com desfoque com um raio de 20 px e um raio de desfoque do plano de fundo de 80 px. Ele tem cantos arredondados, definidos em XML no drawable de segundo plano da janela. Ele corretamente lida com diferentes versões do Android e dispositivos (que potencialmente oferecer suporte a desfoques de janelas) e ativar ou desativar o desfoque no tempo de execução. Garante que o conteúdo da caixa de diálogo seja legível em qualquer uma dessas condições, ajustando o drawable alfa do plano de fundo da janela e o valor de escurecimento da janela.

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 criar cantos arredondados para a janela, definimos o plano de fundo da janela em res/drawable/window_background.xml como um ShapeDrawable com cantos arredondados com raio de 20 dp, da seguinte maneira:

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

Os desfoques de janela desfocam o conteúdo da janela abaixo da atividade. O imagem desfocada é desenhada sob essa janela de atividade, de modo que a a janela de atividade precisa ser translúcida para permitir que o desfoque seja visíveis. Para tornar a janela translúcida, definimos R.attr.windowIsTranslucent na o tema da atividade da seguinte forma:

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

OEMs e parceiros

Para usar o desfoque de janela em um dispositivo, o OEM precisa declarar que o dispositivo oferece suporte a desfoques de janela.

Para verificar se seu dispositivo é compatível com desfoques de janela, faça o seguinte:

  • Verifique se o dispositivo consegue processar a carga extra da GPU. Dispositivos mais simples talvez não consiga processar a carga extra, o que pode causar a queda de frames. Só ative desfoques de janela em dispositivos testados com capacidade de GPU suficiente.

  • Se você tiver um mecanismo de renderização personalizado, verifique se ele implementa a lógica de desfoque. O padrão do Android 12 mecanismo de renderização implementa a lógica de desfoque em BlurFilter.cpp.

Depois de garantir que seu dispositivo seja compatível com desfoques de janela, defina o seguinte: flinger de superfície sysprop:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Validação

Para validar se a janela do app tem processamento adequado ao alternar entre o desfoque ativado e desfocado, siga estas etapas:

  1. Abra a interface com desfoque.

  2. Ative ou desative o desfoque de janela ativando ou desativando o recurso.

  3. Verifique se a interface da janela passa de e para um estado desfocado como esperado.

Ativar e desativar o desfoque de janela

Para testar como a interface da janela é renderizada com o efeito de desfoque da janela, ative ou desativar desfoques usando um dos seguintes métodos:

  • Em "Opções do desenvolvedor":

    Configurações -> Sistema -> Opções do desenvolvedor -> Renderização acelerada por hardware -> Permitir desfoques de janela

  • No terminal de um dispositivo com acesso root:

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

Para verificar se o dispositivo com Android 12 ou versões mais recentes oferece suporte a janelas e se os desfoques de janela estão ativados no momento, execute adb shell wm disable-blur em um dispositivo com acesso root.

Solução de problemas

Use as informações a seguir como um guia para solucionar problemas durante a validação.

Sem desfoque

  • Verifique se os desfoques estão ativados e se o hardware é compatível com eles. Consulte Ativar e desativar o desfoque de janela.

  • Defina uma cor de plano de fundo para a janela translúcida. Uma janela opaca a cor do plano de fundo oculta a área desfocada.

O dispositivo de teste não oferece suporte ao desfoque de janela

  • Teste o aplicativo no emulador do Android 12. Para configurar um Android Emulator, consulte Configurar um Android Emulator. Qualquer dispositivo virtual Android criado com o emulador oferece suporte à janela desfoca.

Sem cantos arredondados

Atualizar a opção para desenvolvedores não ativa desfoques

  • Verifique se o dispositivo está no modo de economia de bateria ou usando encapsulamento de multimídia. Em alguns dispositivos de TV, o desfoque de janela também pode ser desativado durante a reprodução do vídeo.

Desfoque do plano de fundo mostrado em tela cheia, fora dos limites da janela

As atualizações do listener não são aplicadas na tela.

  • As atualizações do listener podem estar sendo aplicadas a uma instância da janela antiga. Verifique se a janela está sendo destruída e recriada com a direita atualização do listener.