FrameLayout: المفاهيم الأساسية، تحديد المواقع البسيط في Android

المؤلف: IT Sectr نُشر: 2026-02-24 وقت القراءة: 8 دق

تعرف على المفاهيم الأساسية لـ FrameLayout — أبسط ViewGroup في Android SDK، المصممة لاستضافة عنصر فرعي واحد (عادةً) أو تراكب عدة عناصر فوق بعضها البعض. FrameLayout مناسب للحاويات المؤقتة، والأجزاء (Fragments)، ومؤشرات التحميل، وعناصر المقدمة. لا يدير وضع العناصر الفرعية — كل عنصر تالٍ يُرسم فوق سابقه، ويتم تحديد موضعه بواسطة layout_gravity (left, top, right, bottom, center). السيناريوهات الأساسية موصوفة في مرجع API الخاص بـ FrameLayout.

الخلاصة

  • تراكب الطبقات — يضع FrameLayout العناصر بشكل تسلسلي واحد فوق الآخر؛ آخر عنصر مصرح به في XML يُرسم في أعلى موضع.
  • المقدمة (Foreground) — السمة android:foreground تسمح بتراكب drawable فوق جميع العناصر الفرعية، مفيد للتأثيرات والإخفاء.
  • measureAllChildren — افتراضياً، يقيس FrameLayout جميع العناصر الفرعية، حتى غير المرئية (GONE). تعطيله يُسرع الرسم بنسبة 20–60%.
  • layout_gravity — الطريقة الوحيدة لوضع عنصر فرعي داخل FrameLayout: top, bottom, left, right, center.
  • الاستخدام مع Fragment — FrameLayout هو الحاوية القياسية لـ FragmentTransaction.replace()، لاستبدال المحتوى دون إعادة إنشاء النشاط.

ما هو FrameLayout؟

FrameLayout هو أبسط ViewGroup في Android SDK، مصمم لحظر منطقة من الشاشة وعرض عرض فرعي واحد (أو تراكب عدة عروض). على عكس LinearLayout (الترتيب التسلسلي) و RelativeLayout (الموضع النسبي)، لا يغير FrameLayout مواضع العناصر الفرعية — يوضع كل عنصر فرعي جديد في الزاوية العلوية اليسرى (0,0) افتراضياً ويُرسَم فوق سابقه.

تم تقديم FrameLayout في API Level 1 ولا يزال أخف حاوية Android: فهو لا يتجاوز onMeasure بمنطق معقد ويقوم بأقل عدد ممكن من عمليات layout. وفقاً لمدونة Android Performance Blog، ينفذ FrameLayout onLayout في تمريرة واحدة ولا يضيف أي حمل إضافي تقريباً مقارنة بوضع View مباشرة. هذا يجعله خياراً مثالياً للحاويات التي تهم فيها السرعة: تخطيطات عناصر RecyclerView (بالاقتران مع ConstraintLayout لتحديد المواقع)، وحاويات Fragment، والطبقات المتداخلة.

يتم تحديد حجم FrameLayout افتراضياً بواسطة أكبر عنصر فرعي (إذا لم يتم تعيين match_parent). إذا لم يتم تحديد أي عنصر فرعي، ينكمش FrameLayout إلى (0,0). السمة android:measureAllChildren (القسم أدناه) تغير هذا السلوك.

FrameLayout في تسلسل ViewGroup في Android

يرث FrameLayout من ViewGroup وهو الأصل المباشر للعديد من الحاويات المتخصصة: FragmentContainerView (الأجزاء)، CardView (البطاقات ذات الظل)، ScrollView (عنصر فرعي واحد)، NestedScrollView. عند إنشاء نشاط مع التنقل عبر Fragment، يستخدم قالب Android Studio القياسي FrameLayout (أو FragmentContainerView) كحاوية جذر للأجزاء.

المقدمة (Foreground) و measureAllChildren

android:foreground — سمة FrameLayout تحدد drawable يُرسَم فوق جميع العناصر الفرعية. على عكس background (تحت العناصر الفرعية)، يُعرَض foreground فوق المحتوى ويمكن أن يكون شفافاً. يُستخدم من أجل: تأثيرات التراكب عند الضغط (ripple عبر ?attr/selectableItemBackground)، إخفاء الصور، عرض مؤشرات الحالة (علامة اختيار على الصورة).

يدعم foreground موارد drawable القياسية: ColorDrawable، ShapeDrawable، RippleDrawable، LayerDrawable. منذ API 23+، يتوفر android:foregroundGravity لتحديد موضع foreground (fill, center, top, bottom). في وضع التعبئة (fill)، يتمدد foreground عبر FrameLayout بأكمله؛ في وضع center، يُرسَم في المنتصف.

android:measureAllChildren — سمة منطقية (true افتراضياً) تحدد ما إذا كان يجب قياس جميع العناصر الفرعية عند حساب حجم FrameLayout. إذا كانت true (افتراضي)، يأخذ FrameLayout في الاعتبار أحجام جميع العناصر الفرعية، بما في ذلك GONE (بحجم 0). إذا كانت false، يقيس FrameLayout فقط العناصر الفرعية VISIBLE و INVISIBLE — تُستبعد عناصر GONE من الحساب. وفقاً لـ Google I/O 2019، تعطيل measureAllChildren للحاويات التي تحتوي على العديد من عناصر GONE (مثل القوائم ذات الرؤية المتغيرة) يُسرع الرسم الأولي بنسبة 20–60%.

layout_gravity: تحديد المواقع داخل FrameLayout

android:layout_gravity — سمة للعنصر الفرعي لـ FrameLayout (و ViewGroups الأخرى) تحدد موضعه داخل الحاوية. في FrameLayout، layout_gravity هو الطريقة الوحيدة للتحكم في موضع العنصر الفرعي، حيث أن FrameLayout لا يوفر قواعد تحديد مواقع خاصة به (مثل RelativeLayout) أو اتجاه (مثل LinearLayout).

القيم الممكنة: top, bottom, left, right, center, center_horizontal, center_vertical, fill, fill_horizontal, fill_vertical, clip_horizontal, clip_vertical. تُدمج باستخدام |: android:layout_gravity="bottom|center_horizontal" — يُلصق العنصر بالحافة السفلية ويُمركز أفقياً. بالنسبة للعناصر الأصغر من FrameLayout، يحدد layout_gravity موضعها في المساحة الحرة.

إذا لم يتم تعيين layout_gravity، يُوضع العنصر في الزاوية العلوية اليسرى (top|left) افتراضياً. بالنسبة لـ FrameLayout الذي يحتوي على عدة عناصر فرعية، يمكن أن يكون لكل عنصر layout_gravity الخاص به — يمكن أن يكون عنصر واحد في الزاوية العلوية اليسرى، وآخر في الزاوية السفلية اليمنى، وثالث في المنتصف. هذا يسمح بإنشاء تراكبات بسيطة (مثل أيقونة إغلاق فوق صورة).

الفرق: gravity vs layout_gravity

android:gravity (سمة الأصل) تحاذي المحتوى داخل FrameLayout — على سبيل المثال، النص داخل TextView. android:layout_gravity (سمة العنصر الفرعي) تحاذي العنصر نفسه داخل FrameLayout. في سياق FrameLayout، تحدد gravity كيفية وضع العناصر الفرعية افتراضياً (مماثل لـ layout_gravity لجميع العناصر الفرعية مرة واحدة)، لكن layout_gravity لكل عنصر محدد يتجاوز قيمة الأصل.

أمثلة: XML وحالات الاستخدام

مثال 1: التراكب الأساسي — تسمية على الصورة

صورة مع تسمية نصية في الزاوية السفلية اليمنى. يحتوي FrameLayout على ImageView يملأ الشاشة و TextView مع layout_gravity="bottom|end" لتحديد الموضع فوق الصورة.

xml
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="200dp">

    <ImageView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@drawable/product_photo"
        android:scaleType="centerCrop" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|end"
        android:layout_margin="8dp"
        android:background="@drawable/badge_background"
        android:elevation="2dp"
        android:paddingHorizontal="8dp"
        android:paddingVertical="4dp"
        android:text="-30%"
        android:textColor="@android:color/white"
        android:textSize="14sp"
        android:textStyle="bold" />

</FrameLayout>

ImageView يملأ FrameLayout بأكمله (ارتفاع 200dp). TextView مع layout_gravity="bottom|end" يُوضع في الزاوية السفلية اليمنى فوق الصورة. elevation=2dp يضيف ظلاً تحت التسمية، ويفصلها بصرياً عن الصورة. هذا المثال البسيط كان سيتطلب حاوية متداخلة أو كوداً مخصصاً في LinearLayout.

مثال 2: مؤشر التحميل (شريط التقدم فوق المحتوى)

شاشة تحتوي على محتوى وشريط تقدم في المنتصف يظهر أثناء التحميل. يحتوي FrameLayout على عنصرين: المحتوى و ProgressBar مع visibility="gone" (يتم التبديل في الكود).

xml
<FrameLayout
    android:id="@+id/content_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/content_text"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:gravity="center"
        android:text="المحتوى المحمل"
        android:textSize="18sp" />

    <ProgressBar
        android:id="@+id/loading_spinner"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_gravity="center"
        android:visibility="gone" />

</FrameLayout>

ProgressBar مخفي افتراضياً (gone). عند بدء التحميل، يتم استدعاء findViewById(R.id.loading_spinner).visibility = View.VISIBLE — يظهر المؤشر في المنتصف فوق المحتوى. بعد التحميل — .visibility = View.GONE. يوفر FrameLayout تراكباً دون تغيير موضع المحتوى — النص لا يتحرك عند ظهور المؤشر، حيث أن ProgressBar يُرسَم فوقه.

مثال 3: حاوية Fragment

FrameLayout كحاوية قياسية لـ FragmentTransaction. يستبدل النشاط الأجزاء داخل هذه الحاوية حسب التنقل.

xml
<FrameLayout
    android:id="@+id/fragment_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent" />
kotlin
import androidx.fragment.app.FragmentTransaction

val fragmentContainer = R.id.fragment_container

fun navigateTo(fragment: Fragment) {
    supportFragmentManager
        .beginTransaction()
        .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
        .replace(fragmentContainer, fragment)
        .addToBackStack(null)
        .commit()
}

FrameLayout كحاوية أجزاء هو الطريقة الأخف لدعم التنقل عبر Fragment. يُوصى باستخدام FragmentContainerView (فئة فرعية من FrameLayout) مع Navigation Component 2.4+، لكن FrameLayout البسيط يظل صالحاً لـ FragmentTransaction اليدوية. الميزة الرئيسية هي أن FragmentTransaction.replace() تستبدل المحتوى بالكامل دون التأثير على النشاط.

متى تستخدم FrameLayout

FrameLayout مثالي لأربعة سيناريوهات: حاوية الأجزاء (FragmentContainerView أو FrameLayout)، تراكب التحميل (ProgressBar فوق المحتوى)، تسميات البطاقات (تسمية على الصورة)، حاوية مؤقتة لـ ViewStub (التحميل الكسول).

غير مناسب لـ: تحديد المواقع المعقد لعدة عناصر (استخدم ConstraintLayout)، القوائم التسلسلية (LinearLayout أو RecyclerView)، التخطيطات الديناميكية مع تغيير ترتيب العناصر.

FrameLayout كعنصر جذر للشاشة — ممارسة سيئة إذا كانت الشاشة تحتوي على أكثر من 2–3 عناصر. للحاوية الجذرية، استخدم ConstraintLayout — فهو يعطي تحكماً أكبر في تحديد المواقع دون فقدان الأداء. استخدم FrameLayout فقط للكتل الفرعية حيث تكون هناك حاجة للتراكب أو الحاوية المؤقتة.

الأسئلة الشائعة

ما الفرق بين FrameLayout و ConstraintLayout؟

FrameLayout — حاوية بسيطة بدون نظام تحديد مواقع (فقط layout_gravity)، مصممة لتراكب العناصر. ConstraintLayout — نظام تخطيط كامل مع القيود (constraints)، والسلاسل، والحواجز، والأبعاد النسبية، ودعم MotionLayout. FrameLayout أسرع للحالات البسيطة (عنصر فرعي واحد أو تراكب)؛ ConstraintLayout ضروري للتخطيطات المعقدة. الاختيار: FrameLayout لتراكب التحميل وحاوية الأجزاء؛ ConstraintLayout لكل شيء آخر.

كيف أجعل FrameLayout يأخذ مساحة أكبر عنصر فرعي فقط؟

اضبط حجم FrameLayout على wrap_content في المحور المطلوب (android:layout_width="wrap_content" و/أو android:layout_height="wrap_content"). سيتكيف FrameLayout بعد ذلك مع حجم أكبر عنصر فرعي. إذا كانت جميع العناصر الفرعية أصغر من FrameLayout، تبقى المساحة الزائدة فارغة. للتحكم الدقيق، استخدم match_parent مع padding ثابت.

لماذا تتداخل العناصر في FrameLayout فوق بعضها البعض؟

هذا هو السلوك المقصود لـ FrameLayout — فهو لا يوزع العناصر الفرعية في المساحة بل يرسمها بشكل تسلسلي واحد فوق الآخر. إذا كنت تريد ألا تتداخل العناصر، استخدم حاوية أخرى (LinearLayout، ConstraintLayout). للتداخل الجزئي مع التحكم في ترتيب الرسم، استخدم layout_gravity للإزاحة و elevation للترتيب Z.

أيهما أسرع: FrameLayout أم ConstraintLayout؟

FrameLayout أسرع للحالات الأبسط — عنصر فرعي واحد، بدون تحديد مواقع معقد. ينفذ FrameLayout onMeasure في وقت ضئيل دون حساب القيود. ومع ذلك، الفرق ملحوظ فقط في مئات التكرارات (RecyclerView بعشرات الآلاف من العناصر). لشاشة نموذجية (1–3 FrameLayout)، الفرق بالميكروثانية ولا يؤثر على تجربة المستخدم. ConstraintLayout هو الخيار العالمي لـ 95% من المهام.

الملخص

  • FrameLayout — أبسط ViewGroup في Android لتراكب العناصر والحاويات المؤقتة بأقل حمل إضافي.
  • تراكب الطبقات — كل عنصر فرعي جديد يُرسَم فوق سابقه؛ آخر عنصر مصرح به في XML هو الأعلى.
  • المقدمة (Foreground) — drawable فوق جميع العناصر الفرعية لتأثيرات التراكب والإخفاء ورسوم ripple.
  • measureAllChildren — تعطيله (false) يستبعد عناصر GONE من حساب الحجم، مما يُسرع الرسم بنسبة 20–60%.
  • layout_gravity — آلية تحديد المواقع الوحيدة: top, bottom, left, right, center ومجموعاتها.
  • حاوية Fragment — حالة الاستخدام الأكثر شيوعاً: FrameLayout (أو FragmentContainerView) لـ FragmentTransaction.replace().
  • الأداء — أسرع حاوية لعنصر فرعي واحد؛ للتخطيطات المعقدة، يتخلف عن ConstraintLayout.

سنقوم بتطوير تطبيق جوال جاهز

تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.

مناقشة المشروع

اقرأ أيضًا