Vết cắt trên màn hình

Android 9 bổ sung hỗ trợ triển khai các kiểu cắt màn hình khác nhau trên thiết bị. Các phần cắt trên màn hình cho phép bạn tạo ra trải nghiệm sống động, tràn viền trong khi vẫn dành không gian cho các cảm biến quan trọng ở mặt trước của thiết bị.

Phần cắt màn hình trung tâm phía trên

Hình 1. Đường cắt màn hình trung tâm phía trên

Android 9 hỗ trợ các loại phần cắt sau:

  • Giữa trên cùng: Phần cắt ở giữa cạnh trên
  • Phần trên không ở giữa: Phần bị cắt có thể ở góc hoặc hơi lệch tâm
  • Dưới cùng: Vết cắt ở phía dưới
  • Kép: Một vết cắt ở trên và một ở dưới

Ví dụ và nguồn

Mã trình quản lý cửa sổ sau đây tại PhoneWindowManager.java cho biết cách các khung hiển thị được chèn vào vùng an toàn khi LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS không được đặt.

// Ensure that windows with a DEFAULT or NEVER display cutout mode are laid out in
// the cutout safe zone.
if (cutoutMode != LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS) {
    final Rect displayCutoutSafeExceptMaybeBars = mTmpDisplayCutoutSafeExceptMaybeBarsRect;
    displayCutoutSafeExceptMaybeBars.set(displayFrames.mDisplayCutoutSafe);
    if (layoutInScreen && layoutInsetDecor && !requestedFullscreen
            && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
        // At the top we have the status bar, so apps that are
        // LAYOUT_IN_SCREEN | LAYOUT_INSET_DECOR but not FULLSCREEN
        // already expect that there's an inset there and we don't need to exclude
        // the window from that area.
        displayCutoutSafeExceptMaybeBars.top = Integer.MIN_VALUE;
    }
    if (layoutInScreen && layoutInsetDecor && !requestedHideNavigation
            && cutoutMode == LAYOUT_IN_DISPLAY_CUTOUT_MODE_DEFAULT) {
        // Same for the navigation bar.
        switch (mNavigationBarPosition) {
            case NAV_BAR_BOTTOM:
                displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
                break;
            case NAV_BAR_RIGHT:
                displayCutoutSafeExceptMaybeBars.right = Integer.MAX_VALUE;
                break;
            case NAV_BAR_LEFT:
                displayCutoutSafeExceptMaybeBars.left = Integer.MIN_VALUE;
                break;
        }
    }
    if (type == TYPE_INPUT_METHOD && mNavigationBarPosition == NAV_BAR_BOTTOM) {
        // The IME can always extend under the bottom cutout if the navbar is there.
        displayCutoutSafeExceptMaybeBars.bottom = Integer.MAX_VALUE;
    }
    // Windows that are attached to a parent and laid out in said parent already avoid
    // the cutout according to that parent and don't need to be further constrained.
    // Floating IN_SCREEN windows get what they ask for and lay out in the full screen.
    // They will later be cropped or shifted using the displayFrame in WindowState,
    // which prevents overlap with the DisplayCutout.
    if (!attachedInParent && !floatingInScreenWindow) {
        mTmpRect.set(pf);
        pf.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
        parentFrameWasClippedByDisplayCutout |= !mTmpRect.equals(pf);
    }
    // Make sure that NO_LIMITS windows clipped to the display don't extend under the
    // cutout.
    df.intersectUnchecked(displayCutoutSafeExceptMaybeBars);
}

SystemUI hiển thị trong vùng bị cắt và cần xác định vị trí có thể vẽ. PhoneStatusBarView.java cung cấp một ví dụ về một khung nhìn xác định vị trí của phần bị cắt trên màn hình, kích thước của nó và liệu phần chèn từ thanh điều hướng có tránh được vùng bị cắt hay không.

Bằng cách ghi đè onApplyWindowInsets() , một chế độ xem có thể xác định vị trí bị cắt và cập nhật bố cục của nó cho phù hợp.

@Override
    public WindowInsets onApplyWindowInsets(WindowInsets insets) {
        if (updateOrientationAndCutout(mLastOrientation)) {
            updateLayoutForCutout();
            requestLayout();
        }
        return super.onApplyWindowInsets(insets);
    }

Các phương pháp này phác thảo cách xử lý các phần cắt ra trên thanh trạng thái trong mọi trường hợp (tức là các phần cắt ở giữa trên cùng, phần trên không ở giữa, phần dưới cùng và phần cắt kép trong tất cả các phép quay).

Yêu cầu

Để đảm bảo ứng dụng không bị ảnh hưởng tiêu cực do bị cắt bỏ, bạn phải đảm bảo rằng:

  • Thanh trạng thái kéo dài ít nhất bằng chiều cao của phần bị cắt ở chế độ dọc
  • Vùng cắt bỏ phải được đóng hộp thư ở chế độ toàn màn hình và ngang

Thiết bị của bạn có thể có tối đa một đường cắt trên mỗi cạnh ngắn (trên và dưới).

Để biết thêm thông tin, hãy xem CDD .

Thực hiện

Để triển khai phần cắt màn hình trên thiết bị của mình, bạn phải định cấu hình các giá trị sau cho Giao diện người dùng hệ thống.

Giá trị Sự miêu tả
quick_qs_offset_height

Xác định lề trên cho bảng cài đặt nhanh. Đồng hồ và pin được hiển thị ở khoảng trống phía trên bảng điều khiển.

Trong vùng đất giá trị, được đặt thành status_bar_height_landscape và trong vùng dọc được đặt thành mặc định là 48dp hoặc chiều cao của phần bị cắt, tùy theo giá trị nào lớn hơn. Có thể tùy ý cao hơn phần cắt ra nếu muốn.

quick_qs_total_height

Tổng chiều cao của bảng cài đặt nhanh-nhanh (bảng cài đặt nhanh được thu gọn) khi ngăn thông báo được mở rộng, bao gồm cả khoảng trống phía trên bảng chứa đồng hồ.

Do cách bố trí cài đặt nhanh, tổng chiều cao của bảng cài đặt nhanh nhanh (bao gồm cả phần bù) phải được xác định tĩnh, do đó giá trị này phải được điều chỉnh theo cùng một delta quick_qs_offset_height . Vùng đất giá trị mặc định giá trị này là 152dp, trong khi mặc định dọc là 176dp.

status_bar_height_portrait

Chiều cao mặc định của thanh trạng thái theo quan điểm của khung.

Trong hầu hết các thiết bị, giá trị này mặc định là 24dp. Khi có phần bị cắt, hãy đặt giá trị này theo chiều cao của phần bị cắt. Có thể tùy ý cao hơn phần cắt ra nếu muốn.

status_bar_height_landscape

Chiều cao của thanh trạng thái ở chế độ ngang. Các phần cắt chỉ được hỗ trợ trên các cạnh ngắn của thiết bị, vì vậy đây sẽ luôn là chiều cao của thanh trạng thái không thay đổi.

Trong một thiết bị không có phần cắt, điều này tương đương với status_bar_height_portrait . Khi có phần bị cắt, hãy giữ giá trị này ở độ cao mặc định của thanh trạng thái.

config_mainBuiltInDisplayCutout

Đường dẫn xác định hình dạng của phần bị cắt. Đây là một chuỗi có thể phân tích cú pháp bởi android.util.PathParser và là cách xác định kích thước cũng như hình dạng của phần bị cắt cho hệ thống.

@dp có thể được chỉ định trên đường dẫn để mô phỏng hình dạng nhắm mục tiêu vào các thiết bị khác nhau. Vì các phần cắt vật lý có kích thước pixel chính xác nên không sử dụng công cụ xác định @dp khi xác định đường dẫn cho rãnh phần cứng.

config_fillMainBuiltinDisplayCutout

Giá trị boolean xác định xem có vẽ đường cắt (được xác định ở trên) trong phần mềm hay không. Có thể được sử dụng để mô phỏng một phần bị cắt hoặc để điền vào một phần bị cắt vật lý để khử răng cưa.

Nếu đúng, config_mainBuiltInDisplayCutout được tô màu đen.

Xem các tệp dimens này để biết các định nghĩa mặc định:

Lớp phủ ví dụ cho phần cắt ra được mô phỏng:

<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">

    <!-- The bounding path of the cutout region of the main built-in display.
         Must either be empty if there is no cutout region, or a string that is parsable by
         {@link android.util.PathParser}.

         The path is assumed to be specified in display coordinates with pixel units and in
         the display's native orientation, with the origin of the coordinate system at the
         center top of the display.

         To facilitate writing device-independent emulation overlays, the marker `@dp` can be
         appended after the path string to interpret coordinates in dp instead of px units.
         Note that a physical cutout should be configured in pixels for the best results.
         -->
    <string translatable="false" name="config_mainBuiltInDisplayCutout">
        M 0,0
        L -48, 0
        L -44.3940446283, 36.0595537175
        C -43.5582133885, 44.4178661152 -39.6, 48.0 -31.2, 48.0
        L 31.2, 48.0
        C 39.6, 48.0 43.5582133885, 44.4178661152 44.3940446283, 36.0595537175
        L 48, 0
        Z
        @dp
    </string>

    <!-- Whether the display cutout region of the main built-in display should be forced to
         black in software (to avoid aliasing or emulate a cutout that is not physically existent).
     -->
    <bool name="config_fillMainBuiltInDisplayCutout">true</bool>

    <!-- Height of the status bar -->
    <dimen name="status_bar_height_portrait">48dp</dimen>
    <dimen name="status_bar_height_landscape">28dp</dimen>
    <!-- Height of area above QQS where battery/time go (equal to status bar height if > 48dp) -->
    <dimen name="quick_qs_offset_height">48dp</dimen>
    <!-- Total height of QQS (quick_qs_offset_height + 128) -->
    <dimen name="quick_qs_total_height">176dp</dimen>

</resources>

Thẩm định

Để xác thực việc triển khai phần cắt màn hình của bạn, hãy chạy kiểm tra CTS tại tests/framework/base/windowmanager/src/android/server/wm .