Rozmycie okna

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

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

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

  • Rozmycie tła umożliwia rozmycie całego ekranu za oknem (dialogowym), tworząc efekt głębi ostrości.

Oba efekty można stosować oddzielnie lub łącznie, jak pokazano na poniższym rysunku:

tylko rozmycie tła,

a

rozmycie tylko z tyłu,

b

rozmycie za i rozmycie tła,

c

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

Funkcja rozmycia okna działa w przypadku wszystkich okien, co oznacza, że działa też wtedy, gdy za oknem znajduje się inna aplikacja. Ten efekt nie jest taki sam jak efekt rozmycia, który rozmywa treści wewnątrz tego samego okna. Rozmycie okna jest przydatne w przypadku okien dialogowych, plansz dolnych i innych okien pływających.

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. Rozmycie o wartości 0 pikseli oznacza brak rozmycia. W przypadku rozmycia za obiektem promień 20 pikseli tworzy dobry efekt głębi ostrości, a promień rozmycia tła 80 pikseli tworzy dobry efekt matowego szkła. Unikaj promieni rozmycia większych niż 150 pikseli, ponieważ znacznie wpłynie to na wydajność.

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

Rozmycie tła

Użyj rozmycia tła w przypadku pływających okien, aby utworzyć efekt tła okna, który jest rozmytym obrazem treści znajdujących się pod nim. Aby dodać rozmyte tło do okna:

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

  2. Ustaw wartość R.attr.windowIsTranslucent na 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 możesz wywołać metodę Window#setBackgroundDrawableResource(int), aby dodać prostokątny obraz tła okna z półprzezroczystym kolorem. Możesz też ustawić R.attr.windowBackground w motywie okna.

  4. W przypadku okna z zaokrąglonymi narożnikami określ zaokrąglone narożniki rozmytego obszaru, ustawiając ShapeDrawablezaokrąglonymi narożnikami jako rysunek tła okna.

  5. Obsługa stanów włączonego i wyłączonego rozmycia. Więcej informacji znajdziesz w sekcji Wytyczne dotyczące używania rozmycia okna w aplikacjach.

Rozmycie za

Rozmycie za rozmywa cały ekran za oknem. Ten efekt jest używany, aby skierować uwagę użytkownika na zawartość okna poprzez rozmycie wszystkiego, co znajduje się 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ć R.attr.windowBlurBehindEnabled w motywie okna.

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

  3. Opcjonalnie wybierz uzupełniającą kwotę przyciemnienia.

  4. Obsługa stanów włączonego i wyłączonego rozmycia. Więcej informacji znajdziesz w sekcji Wytyczne 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 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, np. w trybie oszczędzania baterii, podczas odtwarzania określonych rodzajów treści wideo lub z powodu zastąpienia przez programistę.

Aby aplikacja była kompatybilna z różnymi wersjami Androida, urządzeniami i stanami systemu, postępuj zgodnie z tymi wytycznymi:

  • Dodaj odbiorcę za pomocą funkcji 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.

  • Wdróż 2 wersje tła okna, aby uwzględnić włączony lub wyłączony stan rozmycia okna.

    Gdy rozmycie jest włączone, tło okna powinno być półprzezroczyste, aby rozmycie było widoczne. W tym stanie, gdy rozmycie zostanie wyłączone, zawartość okna będzie bezpośrednio nakładać się na zawartość okna znajdującego się pod nim, co utrudni odczytanie nakładającego się okna. Aby uniknąć takiego efektu, gdy rozmycie okna jest wyłączone, dostosuj interfejs aplikacji w ten sposób:

    • W przypadku rozmycia tła zwiększ wartość alfa rysunku 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 korzysta zarówno z rozmycia tła, jak i rozmycia za elementem.

Poniższy przykład MainActivity.java to okno z rozmyciem za nim o promieniu 20 pikseli i rozmyciem tła o promieniu 80 pikseli. Ma zaokrąglone rogi, zdefiniowane w pliku XML w obiekcie rysowalnym tła okna. Prawidłowo obsługuje różne wersje Androida, różne urządzenia (które mogą nie obsługiwać rozmycia okna) oraz zmiany włączania i wyłączania rozmycia w czasie działania. Dzięki temu zawartość okna dialogowego jest czytelna w każdych warunkach, ponieważ dostosowuje przezroczystość tła okna i stopień przyciemnienia.

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, zdefiniuj tło okna w res/drawable/window_background.xml jako ShapeDrawablezaokrą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 rozmywa zawartość okna pod aktywnością. Rozmyty obraz jest rysowany pod oknem aktywności, więc musi ono być półprzezroczyste, aby rozmycie było widoczne. Aby okno było półprzezroczyste, ustawiamy 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 rozmywanie okien.

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

  • Sprawdź, czy urządzenie poradzi sobie z dodatkowym obciążeniem karty graficznej. Urządzenia z niższej półki mogą nie poradzić sobie z dodatkowym obciążeniem, co może powodować utratę klatek. Włącz rozmycie okien tylko na przetestowanych urządzeniach o wystarczającej mocy GPU.

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

Gdy upewnisz się, że urządzenie obsługuje rozmycie okien, ustaw następujący parametr sysprop:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Weryfikacja

Aby sprawdzić, czy okno aplikacji jest prawidłowo obsługiwane podczas przełączania 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, włączając i wyłączając rozmycie okna.

  3. Sprawdź, czy interfejs okna zmienia się zgodnie z oczekiwaniami z rozmytego na nierozmyty i odwrotnie.

Włączanie i wyłączanie rozmycia okna

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

  • W Opcjach programisty:

    Ustawienia –> System –> Opcje programisty –> Renderowanie z akceleracją sprzętową –> 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 Twoje urządzenie z Androidem w wersji 12 lub nowszej 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

Podczas weryfikacji skorzystaj z poniższych wskazówek.

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.

  • Upewnij się, że kolor tła okna jest półprzezroczysty. Nieprzezroczysty kolor tła okna zasłania rozmyty obszar.

Urządzenie testowe nie obsługuje rozmycia okien

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

Brak zaokrąglonych rogów

  • Ustaw rysunek tła okna, aby zdefiniować zaokrąglone rogi. Ten obiekt rysowalny określa kontur rozmytego obszaru.

Aktualizacja opcji programisty nie włącza 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 rysowane na pełnym ekranie, a nie w granicach okna

Aktualizacje z detektora nie są stosowane na ekranie

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