رسومات مقطوعة للشاشة

يضيف نظام التشغيل Android 9 إمكانية تنفيذ أنواع مختلفة من الفتحات في الشاشة على الأجهزة. تتيح لك الفتحات في الشاشة إنشاء تجربتَي عرض immersive وedge-to-edge مع توفير مساحة للأجهزة المزوّدة بحساسات مهمة في واجهتها.

صورة مقطوعة في أعلى وسط الشاشة

الشكل 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.

التنفيذ

لتنفيذ عمليات اقتصاص الشاشة على جهازك، يجب ضبط القيمة التالية لواجهة المستخدم.

القيمة الوصف
quick_qs_offset_height

يحدِّد هذا العنصر الهامش العلوي للإعدادات السريعة. يتم عرض الساعة والبطارية في المساحة فوق اللوحة.

في قيم الوضع الأفقي، اضبط القيمة على status_bar_height_landscape، وفي الوضع عمودي، اضبط القيمة على القيمة التلقائية 48dp أو على ارتفاع الصورة المخصّصة، whichever is larger. يمكن أن يكون أطول من الفتحة اختياريًا إذا أردت ذلك.

quick_qs_total_height

إجمالي ارتفاع لوحة الإعدادات السريعة (لوحة الإعدادات السريعة المُدمجة) عند توسيع مركز الإشعارات، بما في ذلك المساحة فوق اللوحة التي تحتوي على الساعة

بسبب طريقة عرض الإعدادات السريعة، يجب أن يكون إجمالي ارتفاع لوحة الإعدادات السريعة (بما في ذلك القيمة المتغيرة) معروفًا بشكل ثابت، لذلك يجب تعديل هذه القيمة باستخدام القيمة المتغيرة نفسها 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

قيمة منطقية تحدّد ما إذا كان سيتم رسم مسار القطع (المحدّد أعلاه) في البرنامج. يمكن استخدامها لمحاكاة اقتصاص أو لملء اقتصاص مادي لتحقيق إزالة التمويه.

إذا كانت القيمة صحيحة، يتم ملء 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>

التحقُّق

للتحقّق من صحة تنفيذك لميزة "الفتحات في الشاشة"، يمكنك تنفيذ اختبارات CTS في ملف: tests/framework/base/windowmanager/src/android/server/wm.