عرض المحتوى من الحافة إلى الحافة في المشاهدات

تجربة طريقة Compose
‫Jetpack Compose هي مجموعة أدوات واجهة المستخدم المقترَحة لنظام التشغيل Android. تعرَّف على كيفية استخدام ميزة "العرض على كامل الشاشة" في Compose.

عند استهداف الإصدار 35 من حزمة تطوير البرامج (SDK) أو إصدار أحدث على جهاز يعمل بنظام التشغيل Android 15 أو إصدار أحدث، يتم عرض تطبيقك حتى حافة الشاشة. تمتد النافذة على كامل عرض الشاشة وارتفاعها من خلال الرسم خلف أشرطة النظام. تشمل أشرطة النظام شريط الحالة وشريط العناوين وشريط التنقّل.

تحتوي العديد من التطبيقات على شريط تطبيقات علوي. يجب أن يمتد شريط التطبيق العلوي إلى الحافة العلوية من الشاشة ويظهر خلف شريط الحالة. يمكن بشكل اختياري تصغير شريط التطبيق العلوي إلى ارتفاع شريط الحالة عند الانتقال في المحتوى.

تحتوي العديد من التطبيقات أيضًا على شريط تطبيق سفلي أو شريط تنقّل سفلي. يجب أن تمتد هذه الأشرطة أيضًا إلى الحافة السفلية من الشاشة وتظهر خلف شريط التنقل. وإلا، يجب أن تعرض التطبيقات محتوى قابلاً للتمرير خلف شريط التنقّل.

الشكل 1: أشرطة النظام في تنسيق العرض على الشاشة بأكملها

عند تنفيذ تنسيق يمتد من الحافة إلى الحافة في تطبيقك، يجب مراعاة ما يلي:

  1. تفعيل ميزة "العرض حتى حافة الشاشة"
  2. استخدام التصاميم التكيُّفية لتحسين تجربة المستخدم على أشكال الأجهزة المختلفة
  3. التعامل مع أي تداخلات مرئية
  4. ننصحك بعرض حواجز خلف أشرطة النظام
مثال على الصور خلف شريط الحالة
الشكل 2. مثال على الصور خلف شريط الحالة

تفعيل ميزة "العرض حتى حافة الشاشة"

إذا كان تطبيقك يستهدف الإصدار 35 من حزمة تطوير البرامج (SDK) أو الإصدارات الأحدث، فسيتم تفعيل ميزة "العرض على كامل الشاشة" تلقائيًا على أجهزة Android 15 أو الإصدارات الأحدث.

لتفعيل ميزة "العرض حتى حافة الشاشة" في إصدارات Android السابقة، يمكنك استدعاء enableEdgeToEdge يدويًا في onCreate من Activity.

Kotlin

 override fun onCreate(savedInstanceState: Bundle?) {
         super.onCreate(savedInstanceState)
         WindowCompat.enableEdgeToEdge(window)
        ...
      }

Java

 @Override
      protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        WindowCompat.enableEdgeToEdge(getWindow());
        ...
      }

يجعل enableEdgeToEdge() أشرطة النظام شفافة تلقائيًا، باستثناء وضع التنقّل باستخدام 3 أزرار حيث يحصل شريط التنقّل على حاجب شفاف. يتم تعديل ألوان رموز النظام والغطاء بناءً على مظهر النظام الفاتح أو الداكن.

لتفعيل ميزة "العرض حتى حافة الشاشة" في تطبيقك بدون استخدام دالة enableEdgeToEdge()، يُرجى الاطّلاع على إعداد ميزة "العرض حتى حافة الشاشة" يدويًا.

التعامل مع التداخلات باستخدام الأجزاء الداخلية

قد تظهر بعض طرق العرض في تطبيقك خلف أشرطة النظام، كما هو موضّح في الشكل 3.

يمكنك معالجة التداخلات من خلال التفاعل مع العناصر المضمّنة التي تحدّد أجزاء الشاشة التي تتقاطع مع واجهة مستخدم النظام، مثل شريط التنقّل أو شريط الحالة. يمكن أن يعني التقاطع العرض فوق المحتوى، ولكن يمكنه أيضًا إعلام تطبيقك بإيماءات نظام التشغيل.

في ما يلي أنواع المساحات المتروكة التي تنطبق على عرض تطبيقك من الحافة إلى الحافة:

  • مساحات أشرطة النظام: هي الأفضل لطرق العرض التي يمكن النقر عليها والتي يجب ألا تحجبها أشرطة النظام.

  • صورة مقطوعة للشاشة: للمناطق التي قد تكون فيها صورة مقطوعة للشاشة بسبب شكل الجهاز

  • إيماءات نظام التشغيل: لمناطق التنقّل بالإيماءات التي يستخدمها النظام والتي لها الأولوية على تطبيقك.

أشرطة النظام الداخلية

تُعدّ أشرطة النظام أكثر أنواع الأجزاء الداخلية استخدامًا. وهي تمثّل المنطقة التي تظهر فيها واجهة مستخدم النظام في المحور Z فوق تطبيقك. ويُفضَّل استخدامها لنقل طرق العرض أو إضافة مساحة متروكة إليها في تطبيقك، والتي يمكن النقر عليها ويجب ألا تحجبها أشرطة النظام بصريًا.

على سبيل المثال، زر الإجراء الرئيسي (FAB) في الشكل 3 محجوب جزئيًا بواسطة شريط التنقّل:

مثال على تنفيذ ميزة "العرض على كامل الشاشة"، ولكن شريط التنقّل يغطّي زر الإجراء الرئيسي (FAB)
الشكل 3 شريط التنقّل يتداخل مع زر إجراء عائم في تنسيق يمتد من الحافة إلى الحافة.

لتجنُّب هذا النوع من التداخل المرئي في وضع الإيماءات أو وضع الأزرار، يمكنك زيادة هوامش العرض باستخدام getInsets(int) مع WindowInsetsCompat.Type.systemBars().

يوضّح مثال الرمز التالي كيفية تنفيذ هوامش شريط النظام:

Kotlin

ViewCompat.setOnApplyWindowInsetsListener(fab) { v, windowInsets ->
  val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars())
  // Apply the insets as a margin to the view. This solution sets
  // only the bottom, left, and right dimensions, but you can apply whichever
  // insets are appropriate to your layout. You can also update the view padding
  // if that's more appropriate.
  v.updateLayoutParams<MarginLayoutParams> {
      leftMargin = insets.left
      bottomMargin = insets.bottom
      rightMargin = insets.right
  }

  // Return CONSUMED if you don't want the window insets to keep passing
  // down to descendant views.
  WindowInsetsCompat.CONSUMED
}

Java

ViewCompat.setOnApplyWindowInsetsListener(fab, (v, windowInsets) -> {
  Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemBars());
  // Apply the insets as a margin to the view. This solution sets only the
  // bottom, left, and right dimensions, but you can apply whichever insets are
  // appropriate to your layout. You can also update the view padding if that's
  // more appropriate.
  MarginLayoutParams mlp = (MarginLayoutParams) v.getLayoutParams();
  mlp.leftMargin = insets.left;
  mlp.bottomMargin = insets.bottom;
  mlp.rightMargin = insets.right;
  v.setLayoutParams(mlp);

  // Return CONSUMED if you don't want the window insets to keep passing
  // down to descendant views.
    return WindowInsetsCompat.CONSUMED;
});

إذا طبّقت هذا الحلّ على المثال الموضّح في الشكل 3، لن يحدث أي تداخل مرئي في وضع الزر، كما هو موضّح في الشكل 4:

شريط تنقّل شفاف لا يغطي زر الإجراء العائم
الشكل 4. حلّ مشكلة التداخل المرئي في وضع الأزرار

وينطبق الأمر نفسه على وضع التنقّل بالإيماءات، كما هو موضّح في الشكل 5:

العرض بملء الشاشة بأكملها مع التنقّل بالإيماءات
الشكل 5. حلّ مشكلة التداخل المرئي في وضع التنقّل بالإيماءات

إدراجات الصورة المقطوعة للشاشة

تحتوي بعض الأجهزة على صور مقطوعة للشاشة. عادةً ما يكون الجزء المقتطَع في أعلى الشاشة ومضمّنًا في شريط الحالة. عندما تكون شاشة الجهاز في الوضع الأفقي، قد يكون الجزء المقتطَع على الحافة العمودية. بناءً على المحتوى الذي يعرضه تطبيقك على الشاشة، يجب تنفيذ المساحة المتروكة لتجنُّب الصور المقطوعة للشاشة، لأنّ التطبيقات ستظهر تلقائيًا في الصورة المقطوعة للشاشة.

على سبيل المثال، تعرض العديد من شاشات التطبيقات قائمة بالعناصر. لا تحجب عناصر القائمة باستخدام الصورة المقطوعة للشاشة أو أشرطة النظام.

Kotlin

ViewCompat.setOnApplyWindowInsetsListener(binding.recyclerView) { v, insets ->
  val bars = insets.getInsets(
    WindowInsetsCompat.Type.systemBars()
      or WindowInsetsCompat.Type.displayCutout()
  )
  v.updatePadding(
    left = bars.left,
    top = bars.top,
    right = bars.right,
    bottom = bars.bottom,
  )
  WindowInsetsCompat.CONSUMED
}

Java

ViewCompat.setOnApplyWindowInsetsListener(mBinding.recyclerView, (v, insets) -> {
  Insets bars = insets.getInsets(
    WindowInsetsCompat.Type.systemBars()
    | WindowInsetsCompat.Type.displayCutout()
  );
  v.setPadding(bars.left, bars.top, bars.right, bars.bottom);
  return WindowInsetsCompat.CONSUMED;
});

حدِّد قيمة WindowInsetsCompat من خلال استخدام عامل التشغيل المنطقي أو لأشرطة النظام وأنواع الصور المقطوعة للشاشة.

اضبط clipToPadding على RecyclerView بحيث يتم الانتقال في المساحة المتروكة مع عناصر القائمة. يتيح ذلك للعناصر الانتقال خلف أشرطة النظام عندما يتنقّل المستخدم، كما هو موضّح في المثال التالي.

<androidx.recyclerview.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:clipToPadding="false"
    app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" />

إيماءات نظام التشغيل

تمثّل إيماءات نظام التشغيل المضمّنة مناطق النافذة التي تكون فيها إيماءات نظام التشغيل لها الأولوية على تطبيقك. تظهر هذه المناطق باللون البرتقالي في الشكل 6:

مثال على إيماءات نظام التشغيل المضمّنة
الشكل 6 إيماءات نظام التشغيل

كما هو الحال مع هوامش شريط النظام، يمكنك تجنُّب تداخل هوامش إيماءات نظام التشغيل باستخدام getInsets(int) مع WindowInsetsCompat.Type.systemGestures().

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

في الإصدار 10 من نظام التشغيل Android أو الإصدارات الأحدث، تحتوي العناصر المضمّنة لإيماءات نظام التشغيل على عنصر مضمّن سفلي لإيماءة الشاشة الرئيسية، وعنصر مضمّن أيسر وأيمن لإيماءات الرجوع:

مثال على قياسات إيماءات نظام التشغيل
الشكل 7. قياسات إيماءات نظام التشغيل المضمّنة

يوضّح مثال الرمز التالي كيفية تنفيذ إيماءات نظام التشغيل:

Kotlin

ViewCompat.setOnApplyWindowInsetsListener(view) { view, windowInsets ->
    val insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures())
    // Apply the insets as padding to the view. Here, set all the dimensions
    // as appropriate to your layout. You can also update the view's margin if
    // more appropriate.
    view.updatePadding(insets.left, insets.top, insets.right, insets.bottom)

    // Return CONSUMED if you don't want the window insets to keep passing down
    // to descendant views.
    WindowInsetsCompat.CONSUMED
}

Java

ViewCompat.setOnApplyWindowInsetsListener(view, (v, windowInsets) -> {
    Insets insets = windowInsets.getInsets(WindowInsetsCompat.Type.systemGestures());
    // Apply the insets as padding to the view. Here, set all the dimensions
    // as appropriate to your layout. You can also update the view's margin if
    // more appropriate.
    view.setPadding(insets.left, insets.top, insets.right, insets.bottom);

    // Return CONSUMED if you don't want the window insets to keep passing down
    // to descendant views.
    return WindowInsetsCompat.CONSUMED;
});

مكوّنات Material

تتعامل العديد من مكوّنات Material Design لنظام التشغيل Android (com.google.android.material) المستندة إلى طرق العرض تلقائيًا مع المساحات المتروكة، بما في ذلك BottomAppBar وBottomNavigationView وNavigationRailView وNavigationView

ومع ذلك، لا يتعامل AppBarLayout مع الأجزاء الداخلية تلقائيًا. أضِف android:fitsSystemWindows="true" للتعامل مع الأجزاء العلوية.

يمكنك الاطّلاع على كيفية التعامل مع المساحات المتروكة باستخدام مكوّنات Material في Compose.

إرسال inset متوافق مع الأنظمة القديمة

لإيقاف إرسال الأجزاء المقتطعة إلى طرق العرض الفرعية وتجنُّب الإفراط في استخدام المساحة المتروكة، يمكنك استخدام الأجزاء المقتطعة باستخدام الثابت WindowInsetsCompat.CONSUMED. ومع ذلك، على الأجهزة التي تعمل بنظام التشغيل Android 10 (المستوى 29 من واجهة برمجة التطبيقات والإصدارات الأقدم)، لا يتم إرسال العناصر المضمّنة إلى العناصر التابعة بعد استدعاء WindowInsetsCompat.CONSUMED، ما قد يؤدي إلى تداخل مرئي غير مقصود.

مثال على إرسال إدراج معطّل
الشكل 8 مثال على إرسال غير صحيح لبيانات الحواف لا يتم إرسال المساحات الداخلية إلى طرق العرض الشقيقة بعد أن تستهلك ViewGroup 1 المساحات الداخلية في الإصدار 10 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 29) والإصدارات الأقدم، ما يؤدي إلى تداخل TextView 2 مع شريط التنقّل في النظام. ومع ذلك، يتم إرسال المساحات الداخلية إلى طرق العرض الشقيقة على الإصدار 11 من نظام التشغيل Android (مستوى واجهة برمجة التطبيقات 30) والإصدارات الأحدث، كما هو متوقع.

للتأكّد من إرسال الإطارات الداخلية إلى العناصر الشقيقة في جميع إصدارات Android المتوافقة، استخدِم ViewGroupCompat#installCompatInsetsDispatch قبل استخدام الإطارات الداخلية، وهي متاحة في AndroidX Core وCore-ktx 1.16.0-alpha01 والإصدارات الأحدث.

Kotlin

// Use the i.d. assigned to your layout's root view, e.g. R.id.main
val rootView = findViewById(R.id.main)
// Call before consuming insets
ViewGroupCompat.installCompatInsetsDispatch(rootView)

Java

// Use the i.d. assigned to your layout's root view, e.g. R.id.main
LinearLayout rootView = findViewById(R.id.main);
// Call before consuming insets
ViewGroupCompat.installCompatInsetsDispatch(rootView);
مثال على إرسال العناصر المضمّنة الثابتة
الشكل 9: تم إصلاح مشكلة إرسال الإطارات الداخلية بعد استدعاء ViewGroupCompat#installCompatInsetsDispatch.

الوضع المجسم

يمكن الاستمتاع ببعض المحتوى بشكل أفضل في وضع ملء الشاشة، ما يمنح المستخدم تجربة غامرة أكثر. يمكنك إخفاء أشرطة النظام في الوضع المجسم باستخدام مكتبتَي WindowInsetsController وWindowInsetsControllerCompat:

Kotlin

val windowInsetsController =
      WindowCompat.getInsetsController(window, window.decorView)

// Hide the system bars.
windowInsetsController.hide(Type.systemBars())

// Show the system bars.
windowInsetsController.show(Type.systemBars())

Java

Window window = getWindow();
WindowInsetsControllerCompat windowInsetsController =
      WindowCompat.getInsetsController(window, window.getDecorView());
if (windowInsetsController == null) {
    return;
  }
// Hide the system bars.
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars());

// Show the system bars.
windowInsetsController.show(WindowInsetsCompat.Type.systemBars());

لمزيد من المعلومات حول تنفيذ هذه الميزة، يُرجى الاطّلاع على إخفاء أشرطة النظام في الوضع المجسم.

رموز شريط النظام

يضمن استدعاء enableEdgeToEdge تحديث ألوان رموز شريط النظام عند تغيير مظهر الجهاز.

أثناء استخدام ميزة "العرض على الشاشة بأكملها"، قد تحتاج إلى تعديل ألوان رموز شريط النظام يدويًا لتتباين مع خلفية تطبيقك. على سبيل المثال، لإنشاء رموز شريط حالة فاتحة:

Kotlin

WindowCompat.getInsetsController(window, window.decorView)
    .isAppearanceLightStatusBars = false

Java

WindowCompat.getInsetsController(window, window.getDecorView())
    .setAppearanceLightStatusBars(false);

حماية شريط النظام

بعد أن يستهدف تطبيقك المستوى 35 من حزمة تطوير البرامج (SDK) أو الإصدارات الأحدث، يتم فرض ميزة "العرض على كامل الشاشة". يكون شريط حالة النظام وأشرطة التنقّل بالإيماءات شفافة، ولكنّ شريط التنقّل الذي يتضمّن ثلاثة أزرار يكون شبه شفاف. يُرجى الاتصال بالرقم ‎enableEdgeToEdge لجعل هذا الخيار متوافقًا مع الأنظمة القديمة.

ومع ذلك، قد لا تعمل الإعدادات التلقائية للنظام في بعض حالات الاستخدام. يمكنك الاطّلاع على إرشادات تصميم أشرطة نظام Android والتصميم من الحافة إلى الحافة لتحديد ما إذا كنت تريد استخدام أشرطة نظام شفافة أو شبه شفافة.

إنشاء أشرطة نظام شفافة

يمكنك إنشاء شريط حالة شفاف من خلال استهداف الإصدار 15 من نظام التشغيل Android (الإصدار 35 من حزمة تطوير البرامج) أو الإصدارات الأحدث أو من خلال استدعاء enableEdgeToEdge() باستخدام الوسيطات التلقائية للإصدارات الأقدم.

يمكنك إنشاء شريط تنقّل شفاف للإيماءات من خلال استهداف الإصدار 15 من نظام التشغيل Android أو الإصدارات الأحدث أو من خلال استدعاء enableEdgeToEdge() باستخدام الوسيطات التلقائية للإصدارات الأقدم. بالنسبة إلى شريط التنقّل الذي يتضمّن ثلاثة أزرار، اضبط Window.setNavigationBarContrastEnforced على false، وإلا سيتم تطبيق تظليل شفاف.

إنشاء أشرطة نظام شفافة

لإنشاء شريط حالة شفاف، اتّبِع الخطوات التالية:

  1. يُرجى تحديث تبعية androidx-core إلى الإصدار 1.16.0-beta01 أو إصدار أحدث
  2. أدرِج تنسيق XML في androidx.core.view.insets.ProtectionLayout وحدِّد رقم تعريف.
  3. يمكنك الوصول إلى ProtectionLayout برمجيًا لضبط إجراءات الحماية، وتحديد الجانب وGradientProtection لشريط الحالة.

<androidx.core.view.insets.ProtectionLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list_protection"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ScrollView
        android:id="@+id/item_list"
        android:clipToPadding="false"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <!--items-->

    </ScrollView>

</androidx.core.view.insets.ProtectionLayout>

findViewById<ProtectionLayout>(R.id.list_protection)
    .setProtections(
        listOf(
            GradientProtection(
                WindowInsetsCompat.Side.TOP,
                // Ideally, this is the pane's background color
                paneBackgroundColor
            )
        )
    )

تأكَّد من أنّ قيمة ColorInt التي تم تمريرها إلى GradientProtection تتطابق مع خلفية المحتوى. على سبيل المثال، قد يحتوي عرض على شكل قائمة مع تفاصيل المعروض على جهاز قابل للطي على GradientProtections مختلفة بألوان مختلفة للوحة القائمة ولوحة التفاصيل.

الشكل 1: حماية متدرّجة بألوان مختلفة

لا تنشئ شريط تنقّل بالإيماءات شفافًا. لإنشاء شريط تنقّل شفاف بثلاثة أزرار، يمكنك تنفيذ أحد الإجراءات التالية:

  • إذا كان التنسيق مضمّنًا في ProtectionView، يمكنك تمرير ColorProtection أو GradientProtection إضافي إلى طريقة setProtections. قبل إجراء ذلك، تأكَّد من أنّ window.isNavigationBarContrastEnforced = false.
  • ويمكنك بدلاً من ذلك ضبط السمة window.isNavigationBarContrastEnforced = true.

نصائح أخرى

نصائح إضافية عند التعامل مع الحواف

عرض المحتوى القابل للتمرير من الحافة إلى الحافة

تأكَّد من أنّ العنصر الأخير في القائمة لا تحجبه أشرطة النظام في RecyclerView أو NestedScrollView من خلال معالجة العناصر الداخلية وضبط clipToPadding على false.

يعرض الفيديو التالي جهاز RecyclerView مع إيقاف ميزة "العرض على كامل الشاشة" (على اليمين) وتفعيلها (على اليسار):

يمكنك الاطّلاع على مقتطفات الرموز في قسم إنشاء قوائم ديناميكية باستخدام RecyclerView للحصول على نموذج رمز.

عرض مربّعات الحوار بملء الشاشة حتى حافتها

لجعل مربّع الحوار بملء الشاشة يمتد من الحافة إلى الحافة، يمكنك استدعاء enableEdgeToEdge في مربّع الحوار.

Kotlin

class MyAlertDialogFragment : DialogFragment() {
    override fun onStart(){
        super.onStart()
        dialog?.window?.let { WindowCompat.enableEdgeToEdge(it) }
    }
    ...
}

Java

public class MyAlertDialogFragment extends DialogFragment {
    @Override
    public void onStart() {
        super.onStart();
        Dialog dialog = getDialog();
        if (dialog != null) {
            Window window = dialog.getWindow();
            if (window != null) {
                WindowCompat.enableEdgeToEdge(window);
            }
        }
    }
    ...
}

مراجع إضافية

يمكنك الاطّلاع على المراجع التالية لمزيد من المعلومات حول استخدام ميزة "من الحافة إلى الحافة".

المدوّنات

التصميم

مستندات أخرى

الفيديوهات