Làm mờ cửa sổ

Trong Android 12, các API công khai có sẵn để triển khai các hiệu ứng làm mờ cửa sổ, chẳng hạn như làm mờ nền và làm mờ phía sau.

Làm mờ cửa sổ hoặc làm mờ cửa sổ chéo được sử dụng để làm mờ màn hình phía sau cửa sổ nhất định. Có hai loại làm mờ cửa sổ, có thể được sử dụng để đạt được các hiệu ứng hình ảnh khác nhau:

  • Làm mờ nền cho phép bạn tạo các cửa sổ có nền mờ, tạo hiệu ứng kính mờ.

  • Làm mờ phía sau cho phép bạn làm mờ toàn bộ màn hình phía sau cửa sổ (hộp thoại), tạo hiệu ứng độ sâu trường ảnh.

Hai hiệu ứng này có thể được sử dụng riêng biệt hoặc kết hợp, như thể hiện trong hình sau:

chỉ làm mờ hậu cảnh

Một

chỉ mờ phía sau

b

làm mờ phía sau và làm mờ hậu cảnh

c

Hình 1. Chỉ làm mờ hậu cảnh (a), chỉ làm mờ hậu cảnh (b), làm mờ hậu cảnh và làm mờ hậu cảnh (c)

Tính năng làm mờ cửa sổ hoạt động trên các cửa sổ, nghĩa là tính năng này cũng hoạt động khi có một ứng dụng khác phía sau cửa sổ của bạn. Hiệu ứng này không giống với hiệu ứng làm mờ , làm mờ nội dung bên trong cùng một cửa sổ. Làm mờ cửa sổ rất hữu ích cho các hộp thoại, trang tính dưới cùng và các cửa sổ nổi khác.

Thực hiện

Nhà phát triển ứng dụng

Nhà phát triển ứng dụng phải cung cấp bán kính làm mờ để tạo hiệu ứng làm mờ. Bán kính mờ kiểm soát độ mờ dày đặc, nghĩa là bán kính càng cao thì độ mờ càng dày đặc. Độ mờ 0 px có nghĩa là không bị mờ. Để làm mờ phía sau, bán kính 20 px tạo ra hiệu ứng độ sâu trường ảnh tốt, trong khi bán kính làm mờ hậu cảnh 80 px tạo ra hiệu ứng kính mờ tốt. Tránh bán kính mờ cao hơn 150 px vì điều này sẽ ảnh hưởng đáng kể đến hiệu suất.

Để đạt được hiệu ứng làm mờ mong muốn và tăng khả năng đọc, hãy chọn giá trị bán kính mờ được bổ sung bằng lớp màu mờ.

Làm mờ nền

Sử dụng tính năng làm mờ nền trên các cửa sổ nổi để tạo hiệu ứng nền cửa sổ là hình ảnh mờ của nội dung bên dưới. Để thêm nền mờ cho cửa sổ của bạn, hãy làm như sau:

  1. Gọi Window#setBackgroundBlurRadius(int) để đặt bán kính làm mờ nền. Hoặc, trong chủ đề cửa sổ, đặt R.attr.windowBackgroundBlurRadius .

  2. Đặt R.attr.windowIsTranslucent thành true để làm cho cửa sổ trở nên mờ. Phần mờ được vẽ dưới bề mặt cửa sổ, vì vậy cửa sổ cần phải trong mờ để có thể nhìn thấy phần mờ.

  3. Tùy chọn, hãy gọi Window#setBackgroundDrawableResource(int) để thêm nền cửa sổ hình chữ nhật có thể vẽ được với màu mờ. Hoặc, trong chủ đề cửa sổ, đặt R.attr.windowBackground .

  4. Đối với cửa sổ có các góc tròn, hãy xác định các góc tròn cho vùng bị mờ bằng cách đặt ShapeDrawable với các góc tròn làm nền cửa sổ có thể vẽ được.

  5. Xử lý trạng thái bật và tắt làm mờ. Tham khảo phần Nguyên tắc sử dụng tính năng làm mờ cửa sổ trong ứng dụng để biết thêm thông tin.

Làm mờ phía sau

Hiệu ứng mờ phía sau làm mờ toàn bộ màn hình phía sau cửa sổ. Hiệu ứng này được sử dụng để hướng sự chú ý của người dùng vào nội dung cửa sổ bằng cách làm mờ bất kỳ thứ gì trên màn hình phía sau cửa sổ.

Để làm mờ nội dung phía sau cửa sổ của bạn, hãy làm theo các bước sau:

  1. Thêm FLAG_BLUR_BEHIND vào cờ cửa sổ để bật tính năng làm mờ phía sau. Hoặc, trong chủ đề cửa sổ, đặt R.attr.windowBlurBehindEnabled .

  2. Gọi WindowManager.LayoutParams#setBlurBehindRadius để đặt độ mờ phía sau bán kính. Hoặc, trong chủ đề cửa sổ, đặt R.attr.windowBlurBehindRadius .

  3. Tùy chọn, hãy chọn số lượng mờ bổ sung .

  4. Xử lý trạng thái bật và tắt làm mờ. Tham khảo phần Nguyên tắc sử dụng tính năng làm mờ cửa sổ trong ứng dụng để biết thêm thông tin.

Hướng dẫn sử dụng tính năng làm mờ cửa sổ trong ứng dụng

Hỗ trợ làm mờ cửa sổ phụ thuộc vào những điều sau:

  • Phiên bản Android: API làm mờ cửa sổ chỉ khả dụng trên Android 12 trở lên. Kiểm tra SDK thiết bị cho phiên bản Android.

  • Hiệu suất đồ họa: Các thiết bị có GPU kém hiệu suất hơn có thể chọn không hỗ trợ làm mờ cửa sổ.

  • Trạng thái hệ thống: Máy chủ hệ thống có thể tạm thời vô hiệu hóa tính năng làm mờ cửa sổ trong thời gian chạy, chẳng hạn như trong chế độ tiết kiệm pin, trong khi phát một số loại nội dung video nhất định hoặc do ghi đè của nhà phát triển.

Để làm cho ứng dụng của bạn tương thích trên các phiên bản, thiết bị và trạng thái hệ thống Android, hãy làm theo các nguyên tắc sau:

  • Thêm trình nghe thông qua WindowManager#addCrossWindowBlurEnabledListener để thông báo cho bạn khi tính năng làm mờ cửa sổ được bật hoặc tắt. Ngoài ra, hãy sử dụng WindowManager#isCrossWindowBlurEnabled để truy vấn xem tính năng làm mờ cửa sổ hiện có được bật hay không.

  • Triển khai hai phiên bản cho nền cửa sổ, để phù hợp với trạng thái bật hoặc tắt tính năng làm mờ cửa sổ.

    Khi bật tính năng làm mờ, nền cửa sổ phải trong mờ để có thể nhìn thấy mờ. Ở trạng thái này, khi tính năng làm mờ bị tắt, nội dung cửa sổ sẽ chồng lấp trực tiếp với nội dung của cửa sổ bên dưới, khiến cửa sổ chồng lấp khó đọc hơn. Để tránh hiệu ứng như vậy, khi tính năng làm mờ cửa sổ bị tắt, hãy điều chỉnh giao diện người dùng của ứng dụng như sau:

    • Để làm mờ nền, hãy tăng alpha của nền cửa sổ có thể vẽ được, làm cho nó mờ hơn.

    • Để làm mờ phía sau, hãy thêm một lớp mờ với mức độ mờ cao hơn.

Ví dụ về làm mờ phía sau và làm mờ hậu cảnh

Phần này cung cấp một ví dụ hoạt động về một hoạt động sử dụng cả làm mờ phía sau và làm mờ nền.

Ví dụ sau đây của MainActivity.java là một hộp thoại có bán kính mờ phía sau là 20 px và bán kính mờ nền là 80 px. Nó có các góc tròn, được xác định bằng xml trong nền cửa sổ có thể vẽ được. Nó xử lý chính xác các phiên bản Android khác nhau, các thiết bị khác nhau (có thể không hỗ trợ làm mờ cửa sổ) và các thay đổi được bật hoặc tắt làm mờ thời gian chạy. Nó đảm bảo rằng nội dung hộp thoại có thể đọc được trong bất kỳ điều kiện nào bằng cách điều chỉnh alpha có thể vẽ nền cửa sổ và mức độ mờ của cửa sổ.

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

Để tạo các góc bo tròn cho cửa sổ, chúng ta xác định nền cửa sổ trong res/drawable/window_background.xmlShapeDrawable với các góc bo tròn bán kính 20 dp như sau:

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

Làm mờ cửa sổ làm mờ nội dung của cửa sổ bên dưới hoạt động. Hình ảnh mờ được vẽ bên dưới cửa sổ hoạt động này, vì vậy cửa sổ hoạt động cần phải trong mờ để có thể nhìn thấy phần mờ. Để làm cho cửa sổ trở nên mờ, chúng ta đặt R.attr.windowIsTranslucent trong chủ đề hoạt động như sau:

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

OEM và đối tác

Để làm mờ cửa sổ trên thiết bị, OEM cần khai báo rằng thiết bị đó hỗ trợ làm mờ cửa sổ.

Để kiểm tra xem thiết bị của bạn có hỗ trợ làm mờ cửa sổ hay không, hãy làm như sau:

  • Đảm bảo rằng thiết bị có thể xử lý tải GPU bổ sung. Các thiết bị cấp thấp hơn có thể không có khả năng xử lý tải bổ sung, điều này có thể gây ra tình trạng rớt khung hình. Chỉ bật tính năng làm mờ cửa sổ trên các thiết bị được thử nghiệm có đủ sức mạnh GPU.

  • Nếu bạn có công cụ kết xuất tùy chỉnh, hãy đảm bảo rằng công cụ kết xuất của bạn triển khai logic làm mờ. Công cụ kết xuất mặc định của Android 12 triển khai logic làm mờ trong BlurFilter.cpp .

Khi bạn đảm bảo rằng thiết bị của mình có thể hỗ trợ làm mờ cửa sổ, hãy đặt sysprop bề mặt sau:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Thẩm định

Để xác thực rằng cửa sổ ứng dụng của bạn có khả năng xử lý thích hợp khi chuyển đổi giữa trạng thái bật làm mờ và tắt làm mờ, hãy làm theo các bước sau:

  1. Mở giao diện người dùng bị mờ.

  2. Bật hoặc tắt tính năng làm mờ cửa sổ bằng cách bật và tắt tính năng làm mờ cửa sổ .

  3. Xác minh rằng giao diện người dùng cửa sổ thay đổi sang và từ trạng thái mờ như mong đợi.

Bật và tắt tính năng làm mờ cửa sổ

Để kiểm tra cách giao diện người dùng cửa sổ hiển thị với hiệu ứng làm mờ cửa sổ, hãy bật hoặc tắt tính năng làm mờ bằng một trong các phương pháp sau:

  • Từ Tùy chọn nhà phát triển:

    Cài đặt -> Hệ thống -> Tùy chọn nhà phát triển -> Hiển thị tăng tốc phần cứng -> Cho phép làm mờ cấp độ cửa sổ

  • Từ thiết bị đầu cuối trên thiết bị đã root:

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

Để kiểm tra xem thiết bị Android 12+ của bạn có hỗ trợ tính năng làm mờ cửa sổ hay không và liệu tính năng làm mờ cửa sổ hiện có được bật hay không, hãy chạy adb shell wm disable-blur trên thiết bị đã root.

Xử lý sự cố

Sử dụng nội dung sau làm hướng dẫn khắc phục sự cố trong quá trình xác thực.

Không vẽ mờ

  • Xác minh tính năng làm mờ hiện đã được bật và phần cứng của bạn hỗ trợ chúng. Tham khảo Bật và tắt tính năng làm mờ cửa sổ .

  • Đảm bảo bạn đặt màu nền cửa sổ mờ. Màu nền mờ của cửa sổ sẽ che đi vùng bị mờ.

Thiết bị thử nghiệm không hỗ trợ làm mờ cửa sổ

  • Kiểm tra ứng dụng của bạn trên trình giả lập Android 12. Để thiết lập trình giả lập Android, hãy tham khảo Thiết lập trình giả lập Android . Bất kỳ thiết bị ảo Android nào bạn tạo bằng trình mô phỏng đều hỗ trợ tính năng làm mờ cửa sổ.

Không có góc tròn

Cập nhật tùy chọn nhà phát triển không cho phép làm mờ

  • Kiểm tra xem thiết bị có đang ở chế độ tiết kiệm pin hay đang sử dụng đường hầm đa phương tiện . Trên một số thiết bị TV, tính năng làm mờ cửa sổ cũng có thể bị tắt trong khi phát lại video.

Làm mờ nền được vẽ toàn màn hình, không nằm trong giới hạn cửa sổ

Cập nhật từ trình nghe không được áp dụng trên màn hình

  • Các bản cập nhật trình nghe có thể đang được áp dụng cho phiên bản cửa sổ cũ. Kiểm tra xem cửa sổ có bị phá hủy và được tạo lại bằng bản cập nhật trình nghe phù hợp hay không.