Rozmycie okna

W systemie Android 12 dostępne są publiczne interfejsy API umożliwiające implementację efektów rozmycia okien, takich jak rozmycie tła i rozmycie z tyłu.

Rozmycia okien lub rozmycia między oknami służą do rozmycia ekranu za danym oknem. Istnieją dwa rodzaje rozmyć okien, które można wykorzystać do uzyskania różnych efektów wizualnych:

  • Rozmycie tła umożliwia tworzenie okien z rozmytym tłem, tworząc efekt matowego szkła.

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

Obydwa efekty można stosować oddzielnie lub w połączeniu, jak pokazano na poniższym rysunku:

tylko rozmycie tła

A

rozmycie tylko z tyłu

B

rozmycie tyłu i rozmycie tła

C

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

Funkcja rozmycia okna działa w różnych oknach, co oznacza, że ​​działa również wtedy, gdy za oknem znajduje się inna aplikacja. Ten efekt nie jest taki sam, jak efekt renderowania rozmycia , który rozmywa zawartość w tym samym oknie. Rozmycie okien jest przydatne w przypadku okien dialogowych, dolnych arkuszy i innych ruchomych okien.

Realizacja

Twórcy aplikacji

Twórcy aplikacji muszą podać promień rozmycia, aby uzyskać efekt rozmycia. Promień rozmycia kontroluje gęstość rozmycia, tzn. im większy promień, tym gęstsze jest rozmycie. Rozmycie o wielkości 0 pikseli oznacza brak rozmycia. W przypadku rozmycia tła promień 20 pikseli zapewnia dobry efekt głębi ostrości, natomiast promień rozmycia tła wynoszący 80 pikseli tworzy dobry efekt matowego szkła. Unikaj promieni rozmycia większych niż 150 pikseli, ponieważ będzie to znacząco wpływać 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 ruchomych oknach, aby utworzyć efekt tła okna, który jest rozmytym obrazem podstawowej zawartości. Aby dodać rozmyte tło okna, wykonaj następujące czynności:

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

  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 wywołaj opcję Window#setBackgroundDrawableResource(int) , aby dodać prostokątne tło okna, które można rysować w półprzezroczystym kolorze. Lub w motywie okna ustaw R.attr.windowBackground .

  4. W przypadku okna z zaokrąglonymi narożnikami określ zaokrąglone rogi rozmytego obszaru, ustawiając obiekt ShapeDrawable z zaokrąglonymi narożnikami jako tło okna do rysowania.

  5. Obsługuj stany włączone i wyłączone rozmycie. Aby uzyskać więcej informacji, zapoznaj się z sekcją Wskazówki dotyczące używania rozmycia okien w aplikacjach .

Rozmycie z tyłu

Rozmycie z tyłu rozmywa cały ekran za oknem. Efekt ten służy do skierowania uwagi użytkownika na zawartość okna poprzez rozmycie czegokolwiek na ekranie za oknem.

Aby zamazać zawartość za oknem, wykonaj następujące kroki:

  1. Dodaj FLAG_BLUR_BEHIND do flag okna, aby włączyć rozmycie z tyłu. Lub w motywie okna ustaw R.attr.windowBlurBehindEnabled .

  2. Wywołaj WindowManager.LayoutParams#setBlurBehindRadius aby ustawić rozmycie za promieniem. Lub w motywie okna ustaw R.attr.windowBlurBehindRadius .

  3. Opcjonalnie wybierz uzupełniającą ilość przyciemnienia .

  4. Obsługuj stany włączone i wyłączone rozmycie. Aby uzyskać więcej informacji, zapoznaj się z sekcją Wskazówki dotyczące używania rozmycia okien w aplikacjach .

Wskazówki dotyczące używania rozmycia okien w aplikacjach

Obsługa rozmycia w systemie Windows zależy od następujących czynników:

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

  • 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 okien w czasie wykonywania, na przykład w trybie oszczędzania baterii, podczas odtwarzania niektórych rodzajów treści wideo lub z powodu obejścia programisty.

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

  • Dodaj słuchacza za pomocą WindowManager#addCrossWindowBlurEnabledListener , aby powiadamiać Cię o włączeniu lub wyłączeniu rozmycia okna. Dodatkowo użyj WindowManager#isCrossWindowBlurEnabled aby sprawdzić, czy zamazanie okien jest aktualnie włączone.

  • Zaimplementuj dwie wersje tła okna, aby dostosować się do włączonego lub wyłączonego stanu rozmycia 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 bazowego, przez co nakładające się okno jest mniej czytelne. Aby uniknąć takiego efektu, gdy rozmycie okien jest wyłączone, dostosuj interfejs aplikacji w następujący sposób:

    • Aby uzyskać rozmycie tła, zwiększ wartość alfa rysowanego tła okna, czyniąc je bardziej nieprzezroczystym.

    • Aby rozmyć tył, dodaj przyciemnioną warstwę z większą ilością przyciemnienia.

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

W tej sekcji przedstawiono działający przykład działania, które wykorzystuje zarówno rozmycie tyłu, jak i rozmycie tła.

Poniższy przykład pliku MainActivity.java to okno dialogowe z rozmyciem za promieniem 20 pikseli i promieniem rozmycia tła 80 pikseli. Ma zaokrąglone rogi, zdefiniowane w formacie XML w tle okna do rysowania. Prawidłowo obsługuje różne wersje Androida, różne urządzenia (które potencjalnie nie obsługują rozmycia okien) oraz włączone lub wyłączone rozmycie w czasie wykonywania. Zapewnia czytelność zawartości okna dialogowego w każdych z tych warunków, dostosowując alfa tła okna do rysowania 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 res/drawable/window_background.xml jako ShapeDrawable z zaokrąglonymi narożnikami o promieniu 20 dp w następujący 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 zamazuje zawartość okna pod działaniem. Rozmazany obraz jest rysowany pod tym oknem aktywności, więc okno aktywności musi być półprzezroczyste, aby rozmycie było widoczne. Aby okno było półprzezroczyste, ustawiamy R.attr.windowIsTranslucent w motywie działania w następujący sposób:

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

Producenci OEM i partnerzy

Aby zapewnić rozmycie okien na urządzeniu, producent OEM musi zadeklarować, że urządzenie obsługuje rozmycie okien.

Aby sprawdzić, czy Twoje urządzenie obsługuje rozmycie okien, wykonaj następujące czynności:

  • Upewnij się, że urządzenie wytrzyma dodatkowe obciążenie procesora graficznego. Urządzenia z niższej półki mogą nie wytrzymać dodatkowego obciążenia, co może powodować wypadanie klatek. Włącz rozmycie okien tylko na testowanych urządzeniach z wystarczającą mocą procesora graficznego.

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

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

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Walidacja

Aby sprawdzić, czy okno aplikacji obsługuje się prawidłowo podczas przełączania między stanami z włączonym i wyłączonym rozmyciem, wykonaj następujące kroki:

  1. Otwórz interfejs użytkownika z rozmyciem.

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

  3. Sprawdź, czy interfejs użytkownika okna zmienia się zi na rozmyty zgodnie z oczekiwaniami.

Włącz i wyłącz rozmycie okna

Aby przetestować sposób renderowania interfejsu użytkownika okna z efektem rozmycia okna, włącz lub wyłącz rozmycie, korzystając z jednej z następujących metod:

  • Z opcji programisty:

    Ustawienia -> System -> Opcje programistyczne -> Renderowanie przyspieszane sprzętowo -> Zezwalaj na rozmycie na poziomie okna

  • Z terminala na zrootowanym urządzeniu:

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

Aby sprawdzić, czy Twoje urządzenie z Androidem 12+ obsługuje rozmycie okien i czy rozmycie okien jest obecnie włączone, uruchom adb shell wm disable-blur na zrootowanym urządzeniu.

Rozwiązywanie problemów

Poniższe informacje służą jako wskazówki dotyczące rozwiązywania problemów podczas sprawdzania poprawności.

Nie narysowano rozmycia

  • Sprawdź, czy rozmycia są aktualnie włączone i czy Twój sprzęt je obsługuje. Zobacz Włączanie i wyłączanie rozmycia okna .

  • Upewnij się, że ustawiłeś półprzezroczysty kolor tła okna. Nieprzezroczysty kolor tła okna ukrywa zamazany obszar.

Urządzenie testowe nie obsługuje rozmycia okien

  • Przetestuj swoją aplikację na emulatorze Androida 12. Aby skonfigurować emulator Androida, zapoznaj się z sekcją Konfigurowanie emulatora Androida . Każde urządzenie wirtualne z Androidem, które utworzysz za pomocą emulatora, obsługuje rozmycie okien.

Brak zaokrąglonych narożników

Aktualizacja opcji programisty nie umożliwia rozmycia

  • Sprawdź, czy urządzenie znajduje się w trybie oszczędzania baterii lub czy korzysta z tunelowania multimediów . Na niektórych telewizorach rozmycie okien może być również wyłączone podczas odtwarzania wideo.

Rozmycie tła rysowane na pełnym ekranie, a nie w granicach okna

Aktualizacje ze słuchacza nie są stosowane na ekranie

  • Aktualizacje odbiornika mogą być stosowane do starej instancji okna. Sprawdź, czy okno jest niszczone i odtwarzane za pomocą odpowiedniej aktualizacji odbiornika.