Rozmycie okna

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

Rozmycie okna lub rozmycie między oknami służy do rozmycia ekranu za danym oknem. Dostępne są 2 rodzaje rozmycia okna, które pozwalają uzyskać różne efekty wizualne:

  • Rozmycie tła umożliwia tworzenie okien z rozmytym tłem, co daje efekt matowego szkła.

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

Obu efektów można używać osobno lub razem, jak pokazano na tym rysunku:

tylko rozmycie tła,

a

rozmycie tylko za obiektem,

b

rozmycie tła i rozmycie za obiektem,

c

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

Funkcja rozmycia okna działa we wszystkich oknach, co oznacza, że działa również wtedy, gdy za oknem znajduje się inna aplikacja. Ten efekt różni się od efektu renderowania rozmycia, który rozmywa zawartość wewnątrz tego samego okna. Rozmycie okna jest przydatne w przypadku okien, plansz dolnych i innych pływających okien.

Implementacja

Programiści aplikacji

Aby utworzyć efekt rozmycia, deweloperzy aplikacji muszą podać promień rozmycia. Promień rozmycia określa gęstość rozmycia, czyli im większy promień, tym gęstsze rozmycie. Wartość 0 px oznacza brak rozmycia. W przypadku rozmycia tła promień 20 pikseli daje dobry efekt głębi ostrości, a promień 80 pikseli – dobry efekt matowego szkła. Unikaj promieni rozmycia większych niż 150 pikseli, ponieważ będzie to miało znaczny wpływ 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żywaj rozmycia tła w pływających oknach, aby utworzyć efekt tła okna, który jest rozmytym obrazem treści pod spodem. Aby dodać rozmyte tło do okna, wykonaj te czynności:

  1. Wywołaj Window#setBackgroundBlurRadius(int), aby ustawić promień rozmycia tła. Możesz też ustawić R.attr.windowBackgroundBlurRadius w motywie okna.

  2. Ustaw R.attr.windowIsTranslucent na wartość „true”, aby okno było półprzezroczyste. Rozmycie jest rysowane pod powierzchnią okna, więc okno musi być półprzezroczyste, aby rozmycie było widoczne.

  3. Opcjonalnie wywołaj Window#setBackgroundDrawableResource(int), aby dodać prostokątne tło okna z półprzezroczystym kolorem. Możesz też ustawić R.attr.windowBackground w motywie okna.

  4. W przypadku okna z zaokrąglonymi rogami określ zaokrąglone rogi rozmytego obszaru, ustawiając ShapeDrawable z zaokrąglonymi rogami jako element rysowalny tła okna.

  5. Obsługuj stany włączenia i wyłączenia rozmycia. Więcej informacji znajdziesz w sekcji Wskazówki dotyczące używania rozmycia okna w aplikacjach.

Rozmycie tła

Rozmycie tła rozmywa cały ekran za oknem. Ten efekt służy do kierowania uwagi użytkownika na zawartość okna przez rozmycie wszystkiego na ekranie za oknem.

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

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

  2. Wywołaj funkcję WindowManager.LayoutParams#setBlurBehindRadius, aby ustawić promień rozmycia tła. Możesz też ustawić R.attr.windowBlurBehindRadius w motywie okna.

  3. Opcjonalnie możesz wybrać uzupełniającą wartość przyciemnienia.

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

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

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

  • Wersja Androida: interfejsy API rozmycia okien są dostępne tylko na Androidzie 12 i nowszych. Sprawdź pakiet SDK urządzenia pod kątem wersji Androida.

  • Wydajność grafiki: urządzenia z mniej wydajnymi GPU 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 z różnymi wersjami Androida, urządzeniami i stanami systemu, postępuj zgodnie z tymi wytycznymi:

  • Dodaj detektor za pomocą WindowManager#addCrossWindowBlurEnabledListener, aby otrzymywać powiadomienia o włączeniu lub wyłączeniu rozmycia okna. Dodatkowo użyj 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 okna.

    Gdy rozmycie jest włączone, tło okna powinno być półprzezroczyste, aby rozmycie było widoczne. W tym stanie, gdy rozmycie jest wyłączone, zawartość okna bezpośrednio nakłada się na zawartość okna pod nim, co sprawia, że nakładające się okno jest mniej czytelne. 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 elementu rysowalnego tła okna, aby było bardziej nieprzezroczyste.

    • Aby rozmyć tło, dodaj warstwę przyciemnienia z większą wartością przyciemnienia.

Przykład rozmycia za obiektem i rozmycia tła

Ta sekcja zawiera działający przykład aktywności, która wykorzystuje zarówno rozmycie tła, jak i rozmycie za elementem.

Poniższy przykład MainActivity.java to okno z rozmyciem za promieniem 20 px i rozmyciem tła o promieniu 80 px. Ma zaokrąglone rogi, które są zdefiniowane w pliku XML w elemencie drawable tła okna. Prawidłowo obsługuje różne wersje Androida, różne urządzenia (które mogą nie obsługiwać rozmycia okna) i zmiany włączania lub wyłączania rozmycia w czasie działania. Zapewnia, że treść okna jest czytelna w każdych warunkach, dostosowując przezroczystość 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 rogi okna, definiujemy tło okna w pliku res/drawable/window_background.xml jako ShapeDrawable z zaokrąglonymi rogami o promieniu 20 dp w ten sposób:

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

Rozmycie okna powoduje rozmycie zawartości okna pod aktywnością. Rozmyty obraz jest rysowany pod oknem aktywności, więc okno aktywności musi być półprzezroczyste, aby rozmycie było widoczne. Aby okno było półprzezroczyste, ustaw 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 na urządzeniu można było rozmywać okna, producent OEM musi zadeklarować, że urządzenie obsługuje tę funkcję.

Aby sprawdzić, czy urządzenie obsługuje rozmycie okien:

  • Upewnij się, że urządzenie może obsłużyć dodatkowe obciążenie GPU. Urządzenia niższej klasy mogą nie poradzić sobie z dodatkowym obciążeniem, co może powodować gubienie klatek. Włączaj rozmycie okien tylko na przetestowanych urządzeniach z wystarczającą mocą GPU.

  • Jeśli masz dostosowany silnik renderowania, upewnij się, że implementuje on logikę rozmycia. Domyślny silnik renderowania Androida 12 implementuje logikę rozmycia w BlurFilter.cpp.

Gdy upewnisz się, że urządzenie obsługuje rozmycie okien, ustaw te właściwości SurfaceFlingera sysprop:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Walidacja

Aby sprawdzić, czy okno aplikacji jest prawidłowo obsługiwane podczas przełączania się między stanami z włączonym i wyłączonym rozmyciem, wykonaj te czynności:

  1. Otwórz interfejs z rozmyciem.

  2. Włącz lub wyłącz rozmycie okna, odpowiednio ustawiając przełącznik.

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

Włączanie i wyłączanie rozmycia okna

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

  • W opcjach programisty:

    Ustawienia -> System -> Opcje programisty -> Akceleracja sprzętowa -> Zezwalaj na rozmycie na poziomie okna

  • W terminalu na urządzeniu z dostępem do roota:

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

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

Rozwiązywanie problemów

Poniżej znajdziesz wskazówki dotyczące rozwiązywania problemów podczas weryfikacji.

Nie narysowano rozmycia

  • Sprawdź, czy rozmycie jest obecnie włączone i czy Twój sprzęt je obsługuje. Więcej informacji znajdziesz w artykule Włączanie i wyłączanie rozmycia okna.

  • Ustaw półprzezroczysty kolor tła okna. Nieprzezroczysty kolor tła okna ukrywa rozmyty obszar.

Urządzenie testowe nie obsługuje rozmycia okna

  • Przetestuj aplikację w emulatorze Androida 12. Aby skonfigurować emulator Androida, zapoznaj się z artykułem Konfigurowanie emulatora Androida. Każde urządzenie wirtualne z Androidem utworzone za pomocą emulatora obsługuje rozmycie okna.

Brak zaokrąglonych rogów

Zaktualizowanie opcji dla programistów nie powoduje włączenia rozmycia.

  • Sprawdź, czy urządzenie jest w trybie oszczędzania baterii lub czy korzysta z tunelowania multimediów. Na niektórych telewizorach rozmycie okna może być też wyłączone podczas odtwarzania filmu.

Rozmycie tła jest wyświetlane na pełnym ekranie, a nie w granicach okna

Aktualizacje z odbiornika nie są stosowane na ekranie.

  • Aktualizacje detektora mogą być stosowane do starej instancji okna. Sprawdź, czy okno jest niszczone i ponownie tworzone z odpowiednią aktualizacją detektora.