Làm mờ cửa sổ

Trong Android 12, các API công khai có sẵn để triển khai 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.

Hiệu ứng làm mờ cửa sổ (hoặc làm mờ giữa các cửa sổ) được dùng để làm mờ màn hình phía sau cửa sổ đã cho. Có hai loại hiệu ứng làm mờ cửa sổ mà bạn có thể 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 bị 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 một cửa sổ (hộp thoại), tạo hiệu ứng chiều sâu trường ảnh.

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

chỉ làm mờ nền

a

chỉ làm mờ phía sau

b

làm mờ phía sau và làm mờ nền

c

Hình 1. Chỉ làm mờ nền (a), chỉ làm mờ phía sau (b), làm mờ nền và làm mờ phía sau (c)

Tính năng làm mờ cửa sổ hoạt động trên các cửa sổ, tức 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 kết xuất làm mờ, làm mờ nội dung bên trong cùng một cửa sổ. Hiệu ứng làm mờ cửa sổ rất hữu ích cho hộp thoại, bảng dưới cùng và các cửa sổ nổi khác.

Triển khai

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 làm mờ kiểm soát độ dày của hiệu ứng làm mờ, tức là bán kính càng lớn thì hiệu ứng làm mờ càng dày. Độ mờ 0 px có nghĩa là không có hiệu ứng làm mờ. Đối với hiệu ứng làm mờ phía sau, bán kính 20 px sẽ tạo hiệu ứng độ sâu trường ảnh tốt, trong khi bán kính làm mờ nền 80 px sẽ tạo hiệu ứng kính mờ tốt. Tránh bán kính làm mờ lớn 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 làm mờ được bổ sung bằng một lớp màu mờ.

Làm mờ nền

Sử dụng hiệu ứ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ổ. Đây là hình ảnh mờ của nội dung bên dưới. Để thêm nền mờ cho cửa sổ, 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 giao diện cửa sổ, hãy đặt R.attr.windowBackgroundBlurRadius.

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

  3. Bạn có thể gọi Window#setBackgroundDrawableResource(int) để thêm một đối tượng có thể vẽ nền cửa sổ hình chữ nhật có màu trong suốt. Hoặc trong giao diện cửa sổ, hãy đặt R.attr.windowBackground.

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

  5. Xử lý trạng thái mờ đã bật và đã tắt. Hãy tham khảo phần Nguyên tắc sử dụng hiệu ứ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 làm mờ phía sau sẽ làm mờ toàn bộ màn hình phía sau cửa sổ. Hiệu ứng này được 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ờ mọi thứ trên màn hình phía sau cửa sổ.

Để làm mờ nội dung phía sau cửa sổ, 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 giao diện cửa sổ, hãy đặt R.attr.windowBlurBehindEnabled.

  2. Gọi WindowManager.LayoutParams#setBlurBehindRadius để đặt bán kính làm mờ phía sau. Hoặc trong giao diện cửa sổ, hãy đặt R.attr.windowBlurBehindRadius.

  3. Bạn cũng có thể chọn lượng mờ bổ sung.

  4. Xử lý trạng thái mờ đã bật và đã tắt. Hãy tham khảo phần Nguyên tắc sử dụng hiệu ứng làm mờ cửa sổ trong ứng dụng để biết thêm thông tin.

Nguyên tắc sử dụng hiệu ứng làm mờ cửa sổ trong ứng dụng

Khả năng hỗ trợ hiệu ứng làm mờ cửa sổ phụ thuộc vào những yếu tố sau:

  • Phiên bản Android: Các API làm mờ cửa sổ chỉ có trên Android 12 trở lên. Kiểm tra SDK thiết bị để biết phiên bản Android.

  • Hiệu suất đồ hoạ: Các thiết bị có GPU kém hiệu quả hơn có thể chọn không hỗ trợ hiệu ứng 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 tắt hiệu ứ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 hoặc do nhà phát triển ghi đè.

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

  • Thêm một trình nghe thông qua WindowManager#addCrossWindowBlurEnabledListener để thông báo cho bạn khi hiệu ứng làm mờ cửa sổ được bật hoặc tắt. Ngoài ra, hãy 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 2 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 hiệu ứng làm mờ cửa sổ.

    Khi hiệu ứng làm mờ được bật, nền cửa sổ phải trong mờ để hiệu ứng làm mờ xuất hiện. Ở trạng thái này, khi hiệu ứng làm mờ bị vô hiệu hoá, nội dung cửa sổ sẽ trực tiếp chồng lên nội dung của cửa sổ cơ bản, khiến cửa sổ chồng lên 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:

    • Đối với hiệu ứng làm mờ nền, hãy tăng giá trị alpha của drawable nền cửa sổ để làm cho nề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ề tính năng làm mờ phía sau và làm mờ nền

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

Ví dụ sau đây về MainActivity.java là một hộp thoại có bán kính làm mờ phía sau là 20 px và bán kính làm mờ nền là 80 px. Nó có các góc bo tròn, được xác định trong tệp xml trong đối tượng có thể vẽ nền cửa sổ. Thao tác này 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ợ hiệu ứng làm mờ cửa sổ) và các thay đổi về việc bật hoặc tắt hiệu ứng làm mờ trong thời gian chạy. Thao tác này đảm bảo nội dung hộp thoại có thể đọc được trong bất kỳ điều kiện nào trong số đó bằng cách điều chỉnh alpha của drawable nền cửa sổ và lượng 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.xml dưới dạng một ShapeDrawablecác góc bo tròn với 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>

Hiệu ứng làm mờ cửa sổ sẽ làm mờ nội dung của cửa sổ bên dưới hoạt động. Hình ảnh bị làm 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ờ để cho phép nhìn thấy hiệu ứng làm mờ. Để làm cho cửa sổ trong mờ, chúng ta đặt R.attr.windowIsTranslucent trong giao diện hoạt động như sau:

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

Nhà sản xuất thiết bị gốc và đối tác

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

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

  • Đảm bảo thiết bị có thể xử lý tải GPU bổ sung. Các thiết bị cấp thấp có thể không xử lý được tải trọng bổ sung, dẫn đến tình trạng giảm khung hình. Chỉ bật hiệu ứng làm mờ cửa sổ trên các thiết bị đã thử nghiệm có đủ sức mạnh GPU.

  • Nếu bạn có một công cụ kết xuất tuỳ 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.

Sau khi đảm bảo rằng thiết bị của bạn có thể hỗ trợ hiệu ứng làm mờ cửa sổ, hãy đặt sysprop surface flinger sau đây:

PRODUCT_VENDOR_PROPERTIES += \
       ro.surface_flinger.supports_background_blur=1

Xác nhận kết quả

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

  1. Mở giao diện người dùng có hiệu ứng làm mờ.

  2. Bật hoặc tắt hiệu ứng làm mờ cửa sổ bằng cách bật và tắt hiệu ứng làm mờ cửa sổ.

  3. Xác minh rằng giao diện người dùng của cửa sổ thay đổi thành và từ trạng thái bị mờ như dự kiến.

Bật và tắt hiệu ứng làm mờ cửa sổ

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

  • Trong phần Tuỳ chọn cho nhà phát triển:

    Cài đặt -> Hệ thống -> Tuỳ chọn cho nhà phát triển -> Kết xuất tăng tốc phần cứng -> Cho phép làm mờ ở cấp cửa sổ

  • Trên thiết bị bị can thiệp hệ thống, hãy làm theo các bước sau:

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

Để kiểm tra xem thiết bị Android 12 trở lên có hỗ trợ hiệu ứng làm mờ cửa sổ hay không và hiệu ứ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 một thiết bị đã can thiệp vào hệ thống.

Khắc phục sự cố

Hãy tham khảo hướng dẫn sau đây để khắc phục sự cố trong quá trình xác thực.

Không có hiệu ứng làm mờ nào được vẽ

  • Xác minh rằng bạn đã bật hiệu ứng làm mờ và phần cứng của bạn hỗ trợ hiệu ứng này. Tham khảo bài viết Bật và tắt hiệu ứng làm mờ cửa sổ.

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

Thiết bị kiểm thử không hỗ trợ hiệu ứng làm mờ cửa sổ

  • Kiểm thử ứng dụng của bạn trên trình mô phỏng Android 12. Để thiết lập một trình mô phỏng Android, hãy tham khảo bài viết Thiết lập trình mô phỏng Android. Mọi thiết bị ảo Android mà bạn tạo bằng trình mô phỏng đều hỗ trợ hiệu ứng làm mờ cửa sổ.

Không có góc bo tròn

Việc cập nhật tuỳ chọn cho nhà phát triển không bật tính năng làm mờ

Hiệu ứng làm mờ nền được vẽ ở chế độ toàn màn hình, không nằm trong ranh giới cửa sổ

  • Kiểm tra android:windowIsFloating để đảm bảo cửa sổ của bạn được đánh dấu là cửa sổ nổi.

  • Đảm bảo rằng bạn đã đặt một drawable nền cửa sổ. Chế độ cài đặt này xác định đường viền của vùng làm mờ.

Các bản 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 một phiên bản cửa sổ cũ. Kiểm tra xem cửa sổ có bị huỷ và tạo lại bằng bản cập nhật trình nghe phù hợp hay không.