تعرف على المفاهيم الأساسية لـ FrameLayout — أبسط ViewGroup في Android SDK، المصممة لاستضافة عنصر فرعي واحد (عادةً) أو تراكب عدة عناصر فوق بعضها البعض. FrameLayout مناسب للحاويات المؤقتة، والأجزاء (Fragments)، ومؤشرات التحميل، وعناصر المقدمة. لا يدير وضع العناصر الفرعية — كل عنصر تالٍ يُرسم فوق سابقه، ويتم تحديد موضعه بواسطة layout_gravity (left, top, right, bottom, center). السيناريوهات الأساسية موصوفة في مرجع API الخاص بـ 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 وهو الأصل المباشر للعديد من الحاويات المتخصصة: FragmentContainerView (الأجزاء)، CardView (البطاقات ذات الظل)، ScrollView (عنصر فرعي واحد)، NestedScrollView. عند إنشاء نشاط مع التنقل عبر Fragment، يستخدم قالب Android Studio القياسي FrameLayout (أو FragmentContainerView) كحاوية جذر للأجزاء.
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%.
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 الخاص به — يمكن أن يكون عنصر واحد في الزاوية العلوية اليسرى، وآخر في الزاوية السفلية اليمنى، وثالث في المنتصف. هذا يسمح بإنشاء تراكبات بسيطة (مثل أيقونة إغلاق فوق صورة).
android:gravity (سمة الأصل) تحاذي المحتوى داخل FrameLayout — على سبيل المثال، النص داخل TextView. android:layout_gravity (سمة العنصر الفرعي) تحاذي العنصر نفسه داخل FrameLayout. في سياق FrameLayout، تحدد gravity كيفية وضع العناصر الفرعية افتراضياً (مماثل لـ layout_gravity لجميع العناصر الفرعية مرة واحدة)، لكن layout_gravity لكل عنصر محدد يتجاوز قيمة الأصل.
صورة مع تسمية نصية في الزاوية السفلية اليمنى. يحتوي FrameLayout على ImageView يملأ الشاشة و TextView مع layout_gravity="bottom|end" لتحديد الموضع فوق الصورة.
<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.
شاشة تحتوي على محتوى وشريط تقدم في المنتصف يظهر أثناء التحميل. يحتوي FrameLayout على عنصرين: المحتوى و ProgressBar مع visibility="gone" (يتم التبديل في الكود).
<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 يُرسَم فوقه.
FrameLayout كحاوية قياسية لـ FragmentTransaction. يستبدل النشاط الأجزاء داخل هذه الحاوية حسب التنقل.
<FrameLayout
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
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 مثالي لأربعة سيناريوهات: حاوية الأجزاء (FragmentContainerView أو FrameLayout)، تراكب التحميل (ProgressBar فوق المحتوى)، تسميات البطاقات (تسمية على الصورة)، حاوية مؤقتة لـ ViewStub (التحميل الكسول).
غير مناسب لـ: تحديد المواقع المعقد لعدة عناصر (استخدم ConstraintLayout)، القوائم التسلسلية (LinearLayout أو RecyclerView)، التخطيطات الديناميكية مع تغيير ترتيب العناصر.
FrameLayout كعنصر جذر للشاشة — ممارسة سيئة إذا كانت الشاشة تحتوي على أكثر من 2–3 عناصر. للحاوية الجذرية، استخدم ConstraintLayout — فهو يعطي تحكماً أكبر في تحديد المواقع دون فقدان الأداء. استخدم FrameLayout فقط للكتل الفرعية حيث تكون هناك حاجة للتراكب أو الحاوية المؤقتة.
الأسئلة الشائعة
FrameLayout — حاوية بسيطة بدون نظام تحديد مواقع (فقط layout_gravity)، مصممة لتراكب العناصر. ConstraintLayout — نظام تخطيط كامل مع القيود (constraints)، والسلاسل، والحواجز، والأبعاد النسبية، ودعم MotionLayout. FrameLayout أسرع للحالات البسيطة (عنصر فرعي واحد أو تراكب)؛ ConstraintLayout ضروري للتخطيطات المعقدة. الاختيار: FrameLayout لتراكب التحميل وحاوية الأجزاء؛ ConstraintLayout لكل شيء آخر.
اضبط حجم FrameLayout على wrap_content في المحور المطلوب (android:layout_width="wrap_content" و/أو android:layout_height="wrap_content"). سيتكيف FrameLayout بعد ذلك مع حجم أكبر عنصر فرعي. إذا كانت جميع العناصر الفرعية أصغر من FrameLayout، تبقى المساحة الزائدة فارغة. للتحكم الدقيق، استخدم match_parent مع padding ثابت.
هذا هو السلوك المقصود لـ FrameLayout — فهو لا يوزع العناصر الفرعية في المساحة بل يرسمها بشكل تسلسلي واحد فوق الآخر. إذا كنت تريد ألا تتداخل العناصر، استخدم حاوية أخرى (LinearLayout، ConstraintLayout). للتداخل الجزئي مع التحكم في ترتيب الرسم، استخدم layout_gravity للإزاحة و elevation للترتيب Z.
FrameLayout أسرع للحالات الأبسط — عنصر فرعي واحد، بدون تحديد مواقع معقد. ينفذ FrameLayout onMeasure في وقت ضئيل دون حساب القيود. ومع ذلك، الفرق ملحوظ فقط في مئات التكرارات (RecyclerView بعشرات الآلاف من العناصر). لشاشة نموذجية (1–3 FrameLayout)، الفرق بالميكروثانية ولا يؤثر على تجربة المستخدم. ConstraintLayout هو الخيار العالمي لـ 95% من المهام.
الملخص
سنقوم بتطوير تطبيق جوال جاهز
تقدم IT Sectr تطبيقات iOS وAndroid للشركات الناشئة والشركات منذ عام 2017. سوف نقدم لك النصح ونقترح أفضل حل.