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

يضيف نظام التشغيل 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. تكون القيمة التلقائية لهذه السمة في الوضع الأفقي هي 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>

التحقُّق

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