Flous de fenêtre

Dans Android 12, des API publiques sont disponibles pour implémenter des effets de flou de fenêtre, tels que le flou d'arrière-plan et le flou derrière.

Les flous de fenêtre ou les flous inter-fenêtres sont utilisés pour flouter l'écran derrière la fenêtre donnée. Il existe deux types de flou de fenêtre, qui peuvent être utilisés pour obtenir différents effets visuels :

  • Le flou d'arrière-plan vous permet de créer des fenêtres avec des arrière-plans floutés, ce qui donne un effet de verre dépoli.

  • Flou derrière vous permet de flouter l'intégralité de l'écran derrière une fenêtre (de boîte de dialogue), créant ainsi un effet de profondeur de champ.

Ces deux effets peuvent être utilisés séparément ou combinés, comme illustré dans la figure suivante :

Flou d'arrière-plan uniquement

a

flouter uniquement l'arrière-plan

b

Flou derrière et flou d'arrière-plan

c

Figure 1 : Flou d'arrière-plan uniquement (a), flou derrière uniquement (b), flou d'arrière-plan et flou derrière (c)

La fonctionnalité de flou de la fenêtre fonctionne sur toutes les fenêtres, ce qui signifie qu'elle fonctionne également lorsqu'une autre application se trouve derrière votre fenêtre. Cet effet n'est pas le même que l'effet de rendu de flou, qui floute le contenu à l'intérieur de la même fenêtre. Les flous de fenêtre sont utiles pour les boîtes de dialogue, les bottom sheets et autres fenêtres flottantes.

Implémentation

Développeurs d'applications

Les développeurs d'applications doivent fournir un rayon de flou pour créer un effet de flou. Le rayon de flou contrôle la densité du flou. Plus le rayon est élevé, plus le flou est dense. Un flou de 0 px signifie qu'il n'y a pas de flou. Pour le flou derrière, un rayon de 20 px crée un bon effet de profondeur de champ, tandis qu'un rayon de flou d'arrière-plan de 80 px crée un bon effet de verre dépoli. Évitez les rayons de flou supérieurs à 150 px, car cela aura un impact important sur les performances.

Pour obtenir l'effet de flou souhaité et améliorer la lisibilité, choisissez une valeur de rayon de flou complétée par une couche de couleur translucide.

Flou d'arrière-plan

Utilisez le flou d'arrière-plan sur les fenêtres flottantes pour créer un effet d'arrière-plan de fenêtre, qui est une image floutée du contenu sous-jacent. Pour ajouter un arrière-plan flouté à votre fenêtre :

  1. Appelez Window#setBackgroundBlurRadius(int) pour définir un rayon de flou d'arrière-plan. Vous pouvez également définir R.attr.windowBackgroundBlurRadius dans le thème de la fenêtre.

  2. Définissez R.attr.windowIsTranslucent sur "true" pour rendre la fenêtre translucide. Le flou est dessiné sous la surface de la fenêtre. La fenêtre doit donc être translucide pour que le flou soit visible.

  3. Vous pouvez également appeler Window#setBackgroundDrawableResource(int) pour ajouter un drawable d'arrière-plan de fenêtre rectangulaire avec une couleur translucide. Vous pouvez également définir R.attr.windowBackground dans le thème de la fenêtre.

  4. Pour une fenêtre avec des coins arrondis, déterminez les coins arrondis de la zone floutée en définissant un ShapeDrawable avec des coins arrondis comme drawable d'arrière-plan de la fenêtre.

  5. Gérez les états d'activation et de désactivation du flou. Pour en savoir plus, consultez la section Consignes pour utiliser le flou de fenêtre dans les applications.

Flouter l'arrière-plan

Le flou derrière floute l'intégralité de l'écran derrière la fenêtre. Cet effet est utilisé pour attirer l'attention de l'utilisateur sur le contenu de la fenêtre en floutant tout ce qui se trouve sur l'écran derrière la fenêtre.

Pour flouter le contenu derrière votre fenêtre, procédez comme suit :

  1. Ajoutez FLAG_BLUR_BEHIND aux indicateurs de fenêtre pour activer le flou en arrière-plan. Vous pouvez également définir R.attr.windowBlurBehindEnabled dans le thème de la fenêtre.

  2. Appelez WindowManager.LayoutParams#setBlurBehindRadius pour définir un rayon de flou derrière. Vous pouvez également définir R.attr.windowBlurBehindRadius dans le thème de la fenêtre.

  3. Vous pouvez également choisir un montant de diminution de la luminosité complémentaire.

  4. Gérez les états d'activation et de désactivation du flou. Pour en savoir plus, consultez la section Consignes pour utiliser le flou de fenêtre dans les applications.

Consignes pour utiliser le flou de fenêtre dans les applications

La prise en charge du flou des fenêtres dépend des éléments suivants :

  • Version d'Android : les API de flou des fenêtres ne sont disponibles que sur Android 12 et versions ultérieures. Vérifiez le SDK de l'appareil pour la version d'Android.

  • Performances graphiques : les appareils équipés de GPU moins performants peuvent choisir de ne pas prendre en charge le flou des fenêtres.

  • État du système : le serveur système peut désactiver temporairement le flou des fenêtres au moment de l'exécution, par exemple en mode Économie de batterie, lors de la lecture de certains types de contenus vidéo ou en raison d'une substitution par le développeur.

Pour rendre votre application compatible avec les différentes versions d'Android, les différents appareils et les différents états du système, suivez ces consignes :

  • Ajoutez un écouteur via WindowManager#addCrossWindowBlurEnabledListener pour être averti lorsque les flous de fenêtre sont activés ou désactivés. De plus, utilisez WindowManager#isCrossWindowBlurEnabled pour savoir si les flous de fenêtre sont actuellement activés.

  • Implémentez deux versions pour l'arrière-plan de la fenêtre, afin de tenir compte de l'état activé ou désactivé du flou de la fenêtre.

    Lorsque les flous sont activés, l'arrière-plan de la fenêtre doit être translucide pour que le flou soit visible. Dans cet état, lorsque les flous sont désactivés, le contenu de la fenêtre se chevauche directement avec celui de la fenêtre sous-jacente, ce qui rend la fenêtre qui se chevauche moins lisible. Pour éviter un tel effet, lorsque les flous de fenêtre sont désactivés, adaptez l'UI de l'application comme suit :

    • Pour le flou d'arrière-plan, augmentez le canal alpha de l'élément drawable de l'arrière-plan de la fenêtre pour le rendre plus opaque.

    • Pour le flou derrière, ajoutez un calque de diminution de la luminosité avec un montant de diminution plus élevé.

Exemple de flou derrière et de flou d'arrière-plan

Cette section fournit un exemple concret d'activité qui utilise à la fois le flou derrière et le flou d'arrière-plan.

L'exemple suivant de MainActivity.java est une boîte de dialogue avec un rayon de flou en arrière-plan de 20 px et un rayon de flou d'arrière-plan de 80 px. Il comporte des angles arrondis, définis en XML dans le drawable d'arrière-plan de la fenêtre. Il gère correctement les différentes versions d'Android, les différents appareils (qui ne prennent peut-être pas en charge le flou de fenêtre) et les modifications d'activation ou de désactivation du flou d'exécution. Il garantit que le contenu de la boîte de dialogue est lisible dans toutes ces conditions en ajustant l'alpha de l'élément drawable de l'arrière-plan de la fenêtre et la quantité de luminosité de la fenêtre.

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;
    }
}

Pour créer des angles arrondis pour la fenêtre, nous définissons l'arrière-plan de la fenêtre dans res/drawable/window_background.xml comme ShapeDrawable avec des angles arrondis de rayon 20 dp comme suit :

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

Les flous de fenêtre floutent le contenu de la fenêtre sous l'activité. L'image floutée est dessinée sous cette fenêtre d'activité. La fenêtre d'activité doit donc être translucide pour que le flou soit visible. Pour rendre la fenêtre translucide, nous définissons R.attr.windowIsTranslucent dans le thème de l'activité comme suit :

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

OEM et partenaires

Pour que le flou de fenêtre soit disponible sur un appareil, l'OEM doit déclarer que l'appareil est compatible avec le flou de fenêtre.

Pour vérifier si votre appareil est compatible avec le flou de fenêtre :

  • Assurez-vous que l'appareil peut gérer la charge GPU supplémentaire. Les appareils d'entrée de gamme peuvent ne pas être en mesure de gérer la charge supplémentaire, ce qui peut entraîner une perte d'images. N'activez le flou des fenêtres que sur les appareils testés disposant d'une puissance GPU suffisante.

  • Si vous disposez d'un moteur de rendu personnalisé, assurez-vous qu'il implémente la logique de floutage. Le moteur de rendu Android 12 par défaut implémente la logique de floutage dans BlurFilter.cpp.

Une fois que vous vous êtes assuré que votre appareil peut prendre en charge le flou des fenêtres, définissez les sysprop surface flinger suivants :

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Validation

Pour vérifier que la fenêtre de votre application est correctement gérée lorsque vous activez ou désactivez le flou, procédez comme suit :

  1. Ouvrez l'interface utilisateur contenant le flou.

  2. Activez ou désactivez le flou des fenêtres en activant ou désactivant le flou des fenêtres.

  3. Vérifiez que l'interface utilisateur de la fenêtre passe à l'état flou et inversement comme prévu.

Activer et désactiver le flou des fenêtres

Pour tester le rendu de l'interface utilisateur de la fenêtre avec l'effet de flou, activez ou désactivez le flou à l'aide de l'une des méthodes suivantes :

  • Depuis les options pour les développeurs :

    Paramètres > Système > Options pour les développeurs > Rendu avec accélération matérielle > Autoriser les flous au niveau des fenêtres

  • Depuis le terminal d'un appareil rooté :

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

Pour vérifier si votre appareil Android 12 ou version ultérieure est compatible avec les flous de fenêtre et si ces flous sont actuellement activés, exécutez adb shell wm disable-blur sur un appareil rooté.

Dépannage

Utilisez les informations suivantes comme guide pour résoudre les problèmes lors de la validation.

Aucun flou dessiné

  • Vérifiez que le flou est actuellement activé et que votre matériel le prend en charge. Consultez Activer ou désactiver le floutage des fenêtres.

  • Assurez-vous de définir une couleur d'arrière-plan translucide pour la fenêtre. Une couleur d'arrière-plan de fenêtre opaque masque la zone floutée.

L'appareil de test n'est pas compatible avec le flou de fenêtre

  • Testez votre application sur l'émulateur Android 12. Pour configurer un émulateur Android, consultez Configurer un émulateur Android. Tout appareil virtuel Android que vous créez avec l'émulateur est compatible avec les flous de fenêtre.

Pas de coins arrondis

La modification de l'option pour les développeurs n'active pas le flou

  • Vérifiez si l'appareil est en mode économie d'énergie ou s'il utilise le tunneling multimédia. Sur certains téléviseurs, le flou de la fenêtre peut également être désactivé pendant la lecture de vidéos.

Flou d'arrière-plan dessiné en plein écran, et non dans les limites de la fenêtre

Les mises à jour de l'écouteur ne sont pas appliquées à l'écran

  • Il est possible que les mises à jour de l'écouteur soient appliquées à une ancienne instance de fenêtre. Vérifiez si la fenêtre est détruite et recréée avec la bonne mise à jour du listener.