螢幕凹口

Android 9 增加了在裝置上實現不同類型的顯示切口的支援。顯示器切口可讓您打造身臨其境的無邊體驗,同時仍為裝置正面的重要感應器留出空間。

頂部中央顯示切口

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

Android 9 支援以下類型的剪切:

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

範例和來源

PhoneWindowManager.java中的下列視窗管理器程式碼顯示了在未設定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 測試。