Rozmycie okien

W Androidzie 12 dostępne są publiczne interfejsy API do implementowania efektów rozmywania okna, takich jak rozmycie tła i rozmycie tła okna.

Rozmywanie okna lub rozmywanie między oknami służy do rozmywania ekranu za danym oknem. Dostępne są 2 typy rozmyć okna, które można wykorzystać do uzyskania różnych efektów wizualnych:

  • Rozmycie tła pozwala tworzyć okna z rozmytym tłem, tworząc efekt oszronionego szkła.

  • Rozmycie za oknem pozwala rozmyć cały ekran za oknem (dialogowym), tworząc efekt głębi.

Te 2 efekty można stosować oddzielnie lub razem, jak pokazano na rysunku:

tylko rozmycie tła

a

rozmyj tylko tło

B

rozmycie tła i rozmycia tła

c

Rysunek 1. Tylko rozmycie tła (a), tylko rozmycie tła (b), rozmycie i rozmycie tła (c)

Funkcja rozmycia okna działa w przypadku wszystkich okien, co oznacza, że działa też wtedy, gdy w tle jest inna aplikacja. Ten efekt różni się od efektu rozmycia renderowania, który rozmywa treści w tym samym oknie. Rozmywanie okna jest przydatne w przypadku okien dialogowych, dolnych paneli i innych pływających okien.

Implementacja

Programiści aplikacji

Deweloperzy aplikacji muszą podać promień rozmycia, aby utworzyć efekt rozmycia. Promień rozmycia określa gęstość rozmycia, czyli im większy promień, tym większe rozproszenie. Rozmycie o wartości 0 pikseli oznacza brak rozmycia. Aby rozmyć tło, użyj promienia 20 pikseli, aby uzyskać efekt głębi ostrości, a promień 80 pikseli, aby uzyskać efekt matowego szkła. Unikaj promieni rozmycia przekraczających 150 pikseli, ponieważ znacznie wpływa to na wydajność.

Aby uzyskać pożądany efekt rozmycia i zwiększyć czytelność, wybierz wartość promienia rozmycia uzupełnioną półprzezroczystą warstwą koloru.

Rozmycie tła

Użyj rozmycia tła w oknach pływających, aby utworzyć efekt tła okna, czyli rozmyte zdjęcie zawartości. Aby dodać rozmyte tło okna:

  1. Aby ustawić promień rozmywania tła, wywołaj metodę Window#setBackgroundBlurRadius(int). W motywie okna możesz też ustawić R.attr.windowBackgroundBlurRadius.

  2. Aby okno było półprzezroczyste, ustaw wartość R.attr.windowIsTranslucent na „true” (prawda). Rozmycie jest nakładane pod powierzchnią okna, więc okno musi być półprzezroczyste, aby było widoczne.

  3. Opcjonalnie wywołaj metodę Window#setBackgroundDrawableResource(int), aby dodać prostokątne tło okna, które można rysować w półprzezroczystym kolorze. Możesz też w motywie okna ustawić wartość R.attr.windowBackground.

  4. W przypadku okna z zaokrąglonymi rogami określ zaokrąglone rogi tego obszaru rozmytego, ustawiając rysowanie w trybie shapeDrawable z zaokrąglonymi narożnikami.

  5. Uchwyć stany włączonego i wyłączonego rozmycia. Więcej informacji znajdziesz w sekcji Wskazówki dotyczące korzystania z rozmycia okien w aplikacjach.

Rozmycie w tle

Rozmycie tła rozmywa cały ekran za oknem. Efekt ten służy do kierowania uwagi użytkownika na zawartość okna przez rozmycie dowolnego elementu na ekranie za oknem.

Aby rozmyć zawartość za oknem, wykonaj te czynności:

  1. Dodaj FLAG_BLUR_BEHIND do flag okna, aby włączyć rozmycie tła. Możesz też ustawić w motywie okna ustawienie R.attr.windowBlurBehindEnabled.

  2. Aby ustawić rozmycie za obszarem WindowManager.LayoutParams#setBlurBehindRadius, Możesz też w motywie okna ustawić wartość R.attr.windowBlurBehindRadius.

  3. Opcjonalnie możesz wybrać uzupełniającą kwotę przyciemnioną.

  4. Uchwyć stany włączonego i wyłączonego rozmycia. Więcej informacji znajdziesz w sekcji Wskazówki dotyczące używania rozmycia okna w aplikacjach.

Wskazówki dotyczące korzystania z rozmycia okien w aplikacjach

Obsługa rozmycia okien zależy od tych czynników:

  • Wersja Androida: interfejsy API rozmycia okna są dostępne tylko w Androidzie 12 lub nowszym. Sprawdź wersję Androida w pakiecie SDK urządzenia.

  • Wydajność grafiki: urządzenia z mniej wydajnymi procesorami graficznymi mogą nie obsługiwać rozmycia okien.

  • Stan systemu: serwer systemowy może tymczasowo wyłączyć rozmycie okna w czasie działania, na przykład w trybie oszczędzania baterii, podczas odtwarzania niektórych rodzajów treści wideo lub z powodu zastąpienia przez dewelopera.

Aby zapewnić zgodność aplikacji w przypadku różnych wersji Androida, urządzeń i stanów systemu, postępuj zgodnie z tymi wskazówkami:

  • Dodaj detektor za pomocą funkcji WindowManager#addcrossWindowBlurEnabledListener, który powiadomi Cię o włączeniu lub wyłączeniu rozmycia okien. Dodatkowo użyj zapytania WindowManager#isCrossWindowBlurEnabled, aby sprawdzić, czy rozmycie okna jest obecnie włączone.

  • Zaimplementuj 2 wersje tła okna, aby uwzględnić włączone lub wyłączone rozmycie okien.

    Po włączeniu rozmycia tło okna powinno być przezroczyste, aby było ono widoczne. Gdy rozmycie jest wyłączone, zawartość okna bezpośrednio nakłada się na zawartość okna, co zmniejsza czytelność nakładającego się okna. Aby uniknąć takiego efektu, gdy rozmycie okna jest wyłączone, dostosuj interfejs aplikacji w ten sposób:

    • Aby rozmyć tło, zwiększ wartość alfa rysowanego tła okna, aby stało się ono bardziej nieprzezroczyste.

    • Aby rozmyć tło, dodaj przyciemnioną warstwę o większym stopniu zaciemnienia.

Przykład rozmycia tła i rozmycia tła

W tej sekcji znajdziesz przykład działania, w którym zastosowano zarówno rozmycie tła, jak i rozmycie tła.

Poniższy przykładowy komponent MainActivity.java to okno z promieniem rozmycia tła wynoszącym 20 pikseli i promieniem 80 pikseli. Ma zaokrąglone rogi zdefiniowane w pliku XML w rysowanym tle okna. Prawidłowo obsługuje różne wersje Androida i urządzenia (które potencjalnie nie obsługują rozmycia okien) oraz zmiany z włączonym lub wyłączonym rozmyciem w czasie działania. Gwarantuje to, że treść okna jest czytelna w każdym z tych warunków, dostosowując wartość alfa rysowanego tła okna i stopień przyciemnienia okna.

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

Aby utworzyć zaokrąglone narożniki okna, definiujemy jego tło w narzędziu res/drawable/window_background.xml jako tło kształtowe z zaokrąglonymi narożnikami i promieniem 20 dp:

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

Rozmycie okien pozwala rozmyć zawartość okna pod daną aktywnością. Zamglone zdjęcie jest wyświetlane pod tym oknem aktywności, więc okno aktywności musi być półprzezroczyste, aby umożliwić wyświetlanie zamglenia. Aby okno było przezroczyste, ustawmy atrybut R.attr.windowIsTranslucent w motywie aktywności w ten sposób:

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

Producenci OEM i partnerzy

Aby umożliwić rozmycie okna na urządzeniu, producent OEM musi zadeklarować, że urządzenie obsługuje rozmycie okna.

Aby sprawdzić, czy Twoje urządzenie obsługuje rozmycie okna, wykonaj te czynności:

  • Upewnij się, że urządzenie może obsłużyć dodatkowe obciążenie karty graficznej. Mniejsze urządzenia mogą nie być w stanie obsłużyć dodatkowego obciążenia, co może doprowadzić do utraty klatek. Umożliwiaj rozmycie okna tylko na testowanych urządzeniach z wystarczającą mocą GPU.

  • Jeśli masz niestandardowy mechanizm renderowania, upewnij się, że implementuje on logikę zamazywania. Domyślny silnik renderowania w Androidzie 12 stosuje logikę rozmywania w BlurFilter.cpp.

Po upewnieniu się, że urządzenie może obsługiwać rozmycie okien, ustaw ten element odjazdu powierzchni sysprop:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Weryfikacja

Aby sprawdzić, czy okno aplikacji działa prawidłowo przy przełączaniu między włączonym a wyłączonym rozmyciem, wykonaj te czynności:

  1. Otwórz interfejs z rozmyciem.

  2. Włącz lub wyłącz rozmycie okna, włączając i wyłączając tę funkcję.

  3. Sprawdź, czy interfejs okna zmienia się zgodnie z oczekiwaniami i czy jest rozmyty.

Włączanie i wyłączanie rozmycia okien

Aby sprawdzić, jak interfejs okna będzie się renderował z efektem rozmycia, włącz lub wyłącz rozmycie, korzystając z jednej z tych metod:

  • W Opcjach programisty:

    Ustawienia > System > Opcje programisty > Renderowanie przyspieszone sprzętowo > Zezwalaj na rozmycie na poziomie okna.

  • Z poziomu terminala na urządzeniu z dostępem do roota:

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

Aby sprawdzić, czy Twoje urządzenie z Androidem 12 lub nowszym obsługuje rozmycie okna i czy jest ono włączone, uruchom adb shell wm disable-blur na urządzeniu z dostępem do roota.

Rozwiązywanie problemów

Podczas rozwiązywania problemów z weryfikacją możesz postępować zgodnie z tymi wskazówkami.

Nie ma zastosowanego rozmycia

  • Sprawdź, czy rozmycie jest obecnie włączone i czy Twój sprzęt je obsługuje. Zapoznaj się z artykułem Włączanie i wyłączanie rozmywania okna.

  • Pamiętaj, aby ustawić półprzezroczysty kolor tła okna. Nieprzezroczyste tło okna ukrywa rozmyty obszar.

Urządzenie testowe nie obsługuje rozmycia okien

  • przetestować aplikację na emulatorze Androida 12. Instrukcje konfigurowania emulatora Androida znajdziesz w artykule Konfigurowanie emulatora Androida. Każde urządzenie wirtualne z Androidem utworzone za pomocą emulatora obsługuje rozmycie okien.

Bez zaokrąglonych narożników

Aktualizacja opcji programisty nie włącza rozmyć

  • Sprawdź, czy urządzenie jest w trybie oszczędzania baterii lub czy korzysta z tunelu multimedialnego. Na niektórych urządzeniach telewizyjnych rozmycie okien może być wyłączone podczas odtwarzania filmu.

Rozmycie tła wyświetlane na pełnym ekranie, a nie w obrębie okna

Aktualizacje od detektora nie są stosowane na ekranie

  • Aktualizacje detektora mogą być stosowane do starej instancji okna. Sprawdź, czy okno nie jest niszczone i tworzone ponownie przy odpowiedniej aktualizacji detektora.