顯示切口

Android 9 增加了對在設備上實現不同類型的顯示切口的支持。顯示屏切口可讓您創建身臨其境的邊緣到邊緣體驗,同時仍為設備正面的重要傳感器留出空間。

頂部中央顯示屏切口

圖 1.頂部中央顯示屏切口

Android 9 支持以下類型的摳圖:

  • 頂部中心:頂部邊緣中心的切口
  • 頂部不居中:切口可能位於角落或略微偏離中心
  • 底部:底部鏤空
  • 雙:頂部一個切口,底部一個切口

示例和來源

LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS如何將顯示框架插入到安全區域。

// 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 在摳圖區域呈現,需要確定它可以在哪裡繪製。 PhoneStatusBarView.java提供了一個視圖示例,該視圖確定顯示切口的位置、大小以及導航欄的插圖是否避開切口區域。

通過覆蓋onApplyWindowInsets() ,視圖可以確定剪切的位置並相應地更新其佈局。

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

這些方法概述了在所有情況下如何在狀態欄中處理剪切(即頂部居中、頂部未居中、底部和所有旋轉中的雙剪切)。

要求

為確保應用不受剪裁的負面影響,您必須確保:

  • 狀態欄在縱向模式下至少延伸到切口的高度
  • 剪切區域必須在全屏和橫向模式下進行信箱處理

您的設備在每個短邊(頂部和底部)上最多可以有一個切口。

有關詳細信息,請參閱CDD

執行

要在您的設備上實現顯示切口,您必須為系統 UI 配置以下值。

價值描述
quick_qs_offset_height

定義快速設置面板的上邊距。時鐘和電池顯示在面板上方的空間中。

在 values-land 中,設置為status_bar_height_landscape ,在 Portrait 中設置為默認值 48dp 或切口的高度,以較大者為準。如果需要,可以選擇高於切口。

quick_qs_total_height

展開通知欄時快速設置面板(折疊的快速設置面板)的總高度,包括包含時鐘的面板上方的空間。

由於快速設置的佈局方式,快速快速設置面板的總高度(包括偏移量)必須是靜態已知的,因此該值必須通過相同的 delta quick_qs_offset_height進行調整。 Values-land 默認為 152dp,而縱向默認為 176dp。

status_bar_height_portrait

從框架的角度來看狀態欄的默認高度。

在大多數設備中,這默認為 24dp。當有切口時,將此值設置為切口的高度。如果需要,可以選擇高於切口。

status_bar_height_landscape

橫向狀態欄的高度。切口僅在設備的短邊上受支持,因此這將始終是未更改的狀態欄高度。

在沒有切口的設備中,這相當於status_bar_height_portrait 。當存在切口時,將此值保持在默認狀態欄高度。

config_mainBuiltInDisplayCutout

定義切口形狀的路徑。這是一個可由android.util.PathParser解析的字符串,並且是如何為系統定義切口的大小和形狀的。

@dp可以在路徑上指定以模擬針對不同設備的形狀。因為物理切口具有精確的像素大小,所以在定義硬件槽口的路徑時不要使用@dp說明符。

config_fillMainBuiltinDisplayCutout

一個布爾值,用於確定是否在軟件中繪製剪切路徑(如上定義)。可用於模擬切口,或填充物理切口以實現抗鋸齒。

如果為 true, config_mainBuiltInDisplayCutout填充為黑色。

有關默認定義,請參閱這些dimens文件:

模擬切口的示例疊加層:

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

驗證

要驗證您的顯示切口的實現,請在tests/framework/base/windowmanager/src/android/server/wm運行 CTS 測試。

,

Android 9 增加了對在設備上實現不同類型的顯示切口的支持。顯示屏切口可讓您創建身臨其境的邊緣到邊緣體驗,同時仍為設備正面的重要傳感器留出空間。

頂部中央顯示屏切口

圖 1.頂部中央顯示屏切口

Android 9 支持以下類型的摳圖:

  • 頂部中心:頂部邊緣中心的切口
  • 頂部不居中:切口可能位於角落或略微偏離中心
  • 底部:底部鏤空
  • 雙:頂部一個切口,底部一個切口

示例和來源

LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS如何將顯示框架插入到安全區域。

// 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 在摳圖區域呈現,需要確定它可以在哪裡繪製。 PhoneStatusBarView.java提供了一個視圖示例,該視圖確定顯示切口的位置、大小以及導航欄的插圖是否避開切口區域。

通過覆蓋onApplyWindowInsets() ,視圖可以確定剪切的位置並相應地更新其佈局。

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

這些方法概述了在所有情況下如何在狀態欄中處理剪切(即頂部居中、頂部未居中、底部和所有旋轉中的雙剪切)。

要求

為確保應用不受剪裁的負面影響,您必須確保:

  • 狀態欄在縱向模式下至少延伸到切口的高度
  • 剪切區域必須在全屏和橫向模式下進行信箱處理

您的設備在每個短邊(頂部和底部)上最多可以有一個切口。

有關詳細信息,請參閱CDD

執行

要在您的設備上實現顯示切口,您必須為系統 UI 配置以下值。

價值描述
quick_qs_offset_height

定義快速設置面板的上邊距。時鐘和電池顯示在面板上方的空間中。

在 values-land 中,設置為status_bar_height_landscape ,在 Portrait 中設置為默認值 48dp 或切口的高度,以較大者為準。如果需要,可以選擇高於切口。

quick_qs_total_height

展開通知欄時快速設置面板(折疊的快速設置面板)的總高度,包括包含時鐘的面板上方的空間。

由於快速設置的佈局方式,快速快速設置面板的總高度(包括偏移量)必須是靜態已知的,因此該值必須通過相同的 delta quick_qs_offset_height進行調整。 Values-land 默認為 152dp,而縱向默認為 176dp。

status_bar_height_portrait

從框架的角度來看狀態欄的默認高度。

在大多數設備中,這默認為 24dp。當有切口時,將此值設置為切口的高度。如果需要,可以選擇高於切口。

status_bar_height_landscape

橫向狀態欄的高度。切口僅在設備的短邊上受支持,因此這將始終是未更改的狀態欄高度。

在沒有切口的設備中,這相當於status_bar_height_portrait 。當存在切口時,將此值保持在默認狀態欄高度。

config_mainBuiltInDisplayCutout

定義切口形狀的路徑。這是一個可由android.util.PathParser解析的字符串,並且是如何為系統定義切口的大小和形狀的。

@dp可以在路徑上指定以模擬針對不同設備的形狀。因為物理切口具有精確的像素大小,所以在定義硬件槽口的路徑時不要使用@dp說明符。

config_fillMainBuiltinDisplayCutout

一個布爾值,用於確定是否在軟件中繪製剪切路徑(如上定義)。可用於模擬切口,或填充物理切口以實現抗鋸齒。

如果為 true, config_mainBuiltInDisplayCutout填充為黑色。

有關默認定義,請參閱這些dimens文件:

模擬切口的示例疊加層:

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

驗證

要驗證您的顯示切口的實現,請在tests/framework/base/windowmanager/src/android/server/wm運行 CTS 測試。