Borrões da janela

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

Desfoques de janela, ou desfoques entre janelas, são usados ​​para desfocar a tela atrás de uma determinada janela. Existem dois tipos de desfoque de janela, que podem ser usados ​​para obter diferentes efeitos visuais:

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

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

Os dois efeitos podem ser usados ​​separadamente ou combinados, conforme mostrado na figura a seguir:

apenas desfoque de fundo

a

desfocar apenas atrás

b

desfoque atrás e desfoque de fundo

c

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

O recurso de desfoque de janela funciona em janelas, o que significa que também funciona quando há outro aplicativo atrás de sua janela. Este efeito não é o mesmo que o efeito de renderização de desfoque , que desfoca o conteúdo dentro da mesma janela. Desfoques de janela são úteis para caixas de diálogo, folhas inferiores e outras janelas flutuantes.

Implementação

Desenvolvedores de aplicativos

Os desenvolvedores de aplicativos devem fornecer um raio de desfoque para criar um efeito de desfoque. O raio do desfoque controla o quão denso é o desfoque, ou seja, quanto maior o raio, mais denso é o desfoque. Um desfoque de 0 px significa que não há desfoque. Para desfoque atrás, um raio de 20 px cria um bom efeito de profundidade de campo, enquanto um raio de desfoque de fundo de 80 px cria um bom efeito de vidro fosco. Evite raios de desfoque superiores a 150 px, pois isso afetará significativamente o desempenho.

Para obter o efeito de desfoque desejado e aumentar a legibilidade, escolha um valor de raio de desfoque complementado com uma camada de cor translúcida.

Desfoque de fundo

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

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

  2. Defina R.attr.windowIsTranslucent como true para tornar a janela translúcida. O desfoque é desenhado sob a superfície da janela, portanto a janela precisa ser translúcida para permitir que o desfoque seja visível.

  3. Opcionalmente, chame Window#setBackgroundDrawableResource(int) para adicionar um fundo de janela retangular que pode ser desenhado 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 a área desfocada definindo um ShapeDrawable com cantos arredondados como o desenhável de fundo da janela.

  5. Lidar com estados de desfoque ativado e desativado. Consulte a seção Diretrizes para usar o desfoque de janela em aplicativos para obter mais informações.

Desfocar atrás

O desfoque atrás desfoca toda a tela atrás da janela. Este efeito é usado para 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 sua janela, siga estas etapas:

  1. Adicione FLAG_BLUR_BEHIND aos sinalizadores da janela, para ativar o desfoque atrás. Ou, no tema da janela, defina R.attr.windowBlurBehindEnabled .

  2. Chame WindowManager.LayoutParams#setBlurBehindRadius para definir um desfoque atrás do raio. Ou, no tema da janela, defina R.attr.windowBlurBehindRadius .

  3. Opcionalmente, escolha um valor dim complementar.

  4. Lidar com estados de desfoque ativado e desativado. Consulte a seção Diretrizes para usar o desfoque de janela em aplicativos para obter mais informações.

Diretrizes para usar o desfoque de janela em aplicativos

O suporte para desfoque de janelas depende do seguinte:

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

  • Desempenho gráfico: dispositivos com GPUs de menor desempenho podem optar por não suportar desfoques de janela.

  • Estado do sistema: o servidor do sistema pode desativar temporariamente o desfoque da janela em tempo de execução, por exemplo, durante o modo de economia de bateria, durante a reprodução de determinados tipos de conteúdo de vídeo ou devido a uma substituição do desenvolvedor.

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

  • Adicione um ouvinte por meio de WindowManager#addCrossWindowBlurEnabledListener , para notificá-lo quando os desfoques da janela estiverem ativados ou desativados. Além disso, use WindowManager#isCrossWindowBlurEnabled para consultar se os desfoques de janela estão ativados no momento.

  • Implemente duas versões para o plano de fundo da janela, para acomodar o estado ativado ou desativado dos desfoques da janela.

    Quando o desfoque está ativado, o fundo da janela deve ser translúcido para tornar o desfoque visível. Nesse estado, quando o desfoque é desativado, o conteúdo da janela se sobrepõe diretamente ao conteúdo da janela subjacente, tornando a janela sobreposta menos legível. Para evitar esse efeito, quando o desfoque da janela estiver desativado, adapte a interface do aplicativo da seguinte maneira:

    • Para desfoque de fundo, aumente o alfa do drawable de fundo da janela, tornando-o mais opaco.

    • Para desfocar atrás, adicione uma camada escura com uma quantidade maior de escuridão.

Exemplo de desfoque atrás e desfoque de fundo

Esta seção fornece um exemplo prático de uma atividade que usa desfoque atrás e desfoque de fundo.

O exemplo a seguir de MainActivity.java é uma caixa de diálogo com um raio de desfoque atrás de 20 px e um raio de desfoque de fundo de 80 px. Possui cantos arredondados, definidos em xml no drawable de fundo da janela. Ele lida corretamente com diferentes versões do Android, diferentes dispositivos (que potencialmente não suportam desfoques de janela) e alterações ativadas ou desativadas de desfoque de tempo de execução. Ele garante que o conteúdo da caixa de diálogo seja legível sob qualquer uma dessas condições, ajustando o alfa desenhável do plano de fundo da janela e a quantidade 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 fundo da janela em res/drawable/window_background.xml como um ShapeDrawable com cantos arredondados com raio de 20 dp da seguinte forma:

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

Desfoques de janela desfocam o conteúdo da janela abaixo da atividade. A imagem desfocada é desenhada sob esta janela de atividade, portanto a janela de atividade precisa ser translúcida para permitir que o desfoque seja visível. Para tornar a janela translúcida, definimos R.attr.windowIsTranslucent no 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 ter desfoque de janela em um dispositivo, o OEM precisa declarar que o dispositivo suporta desfoque de janela.

Para verificar se o seu dispositivo suporta desfoques de janela, faça o seguinte:

  • Certifique-se de que o dispositivo possa lidar com a carga extra da GPU. Dispositivos de baixo custo podem não ser capazes de lidar com a carga extra, o que pode causar queda de quadros. Ative o desfoque de janela apenas em dispositivos testados com potência de GPU suficiente.

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

Depois de garantir que seu dispositivo pode suportar desfoques de janela, defina o seguinte defletor de superfície sysprop :

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Validação

Para validar se a janela do seu aplicativo tem o tratamento adequado ao alternar entre os estados de desfoque ativado e desfoque desativado, siga estas etapas:

  1. Abra a IU que está desfocada.

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

  3. Verifique se a interface do usuário da janela muda de e para um estado desfocado conforme esperado.

Ativar e desativar o desfoque da janela

Para testar como a IU da janela é renderizada com o efeito de desfoque de janela, habilite ou desabilite os desfoques usando um dos seguintes métodos:

  • Nas opções do desenvolvedor:

    Configurações -> Sistema -> Opções do desenvolvedor -> Renderização acelerada por hardware -> Permitir desfoques no nível da janela

  • Do terminal em um dispositivo com acesso root:

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

Para verificar se o seu dispositivo Android 12+ suporta desfoques de janela 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 o seguinte como guia para solução de problemas durante a validação.

Nenhum desfoque desenhado

  • Verifique se os desfoques estão ativados e se o seu hardware os suporta. Consulte Ativar e desativar o desfoque da janela .

  • Certifique-se de definir uma cor de fundo translúcida para a janela. Uma cor de fundo de janela opaca esconde a área desfocada.

O dispositivo de teste não suporta desfoques de janela

  • Teste seu aplicativo no emulador do Android 12. Para configurar um emulador Android, consulte Configurar um emulador Android . Qualquer dispositivo virtual Android criado com o emulador suporta desfoques de janela.

Sem cantos arredondados

Atualizar a opção do desenvolvedor não permite desfoques

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

Desfoque de fundo desenhado em tela cheia, fora dos limites da janela

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

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