ConstraintLayout — ما هو، القيود والتسلسل الهرمي المسطح

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

نتعرف على ما هو ConstraintLayout — نظام تحديد مواضع مرن لنظام Android يتيح بناء تسلسلات هرمية مسطحة للعرض باستخدام القيود (constraints) بدلاً من LinearLayout و RelativeLayout المتداخلة. يحل ConstraintLayout مشكلة "التداخل الجهنمي" (layout nesting hell)، مما يقلل عمق التسلسل الهرمي إلى مستوى واحد ويسرع عرض الشاشة. المكتبة جزء من Jetpack ومتاحة بدءاً من Android 2.3 (API 9) عبر support-library. الآليات الرئيسية موصوفة في التوثيق الرسمي لنظام Android.

النقاط الرئيسية

  • تسلسل هرمي مسطح — يتيح ConstraintLayout بناء واجهات بأي درجة تعقيد دون حاويات متداخلة، مما يسرع onMeasure و onLayout بمقدار 2–3 مرات.
  • القيود — تحديد مواضع العناصر عبر ربط الحواف (layout_constraintLeft_toRightOf, layout_constraintTop_toBottomOf) بالأصل أو بعروض أخرى.
  • Chain و Guideline — توزع السلاسل العناصر بالتساوي أو حسب الوزن؛ تحدد الخطوط الإرشادية هوامش تناسبية بنسب مئوية.
  • Barrier و Group — يتكيف الحاجز ديناميكياً مع حجم مجموعة العناصر؛ تدير Group رؤية عروض متعددة في وقت واحد.
  • MotionLayout — فئة فرعية من ConstraintLayout لتحريك الانتقالات بين حالات القيود مع دعم KeyFrame.

ما هو ConstraintLayout؟

ConstraintLayout هو ViewGroup من مكتبة AndroidX ConstraintLayout، مصمم لإنشاء واجهات مرنة وعالية الأداء من خلال قيود تصريحية. على عكس LinearLayout الذي يرتب العناصر في سطر واحد، أو RelativeLayout الذي يحدد مواضع العناصر بالنسبة للمجاورة، يتيح ConstraintLayout تثبيت كل عنصر بالنسبة لأي عناصر أخرى والأصل في وقت واحد.

تم الإعلان عن المكتبة في Google I/O 2016 كحل لتسريع عرض الشاشات المعقدة. المشكلة الرئيسية التي يحلها ConstraintLayout هي تداخل التخطيطات. كل ViewGroup متداخلة تضيف على الأقل تمريرتين measure وتمريرة layout واحدة. شاشة ذات 4 مستويات من التداخل تنفذ 8 تمريرات measure؛ ConstraintLayout بنفس الوظائف ينفذ تمريرتين فقط. وفقاً لـ Google (Android Performance Blog, 2017)، استبدال ثلاثة LinearLayouts متداخلة بـ ConstraintLayout واحد يقلل وقت onMeasure بنسبة 40%.

الإصدار الحالي ConstraintLayout 2.1.4 يعمل بثبات على Android 2.3+ (API 9) عبر AndroidX. الإصدار 2.0 قدم تحديد المواقع الدائري و Flow (التفاف العناصر تلقائياً) ودعم MotionLayout. ConstraintLayout ضروري لفهم تطوير Android الحديث — يُستخدم في Jetpack Compose كمفهوم أساسي للمعدّلات، وفي قوالب Android Studio الافتراضية، وفي Material Design 3.

كيف يعمل التسلسل الهرمي المسطح

التسلسل الهرمي المسطح لـ ConstraintLayout يعني أن جميع العروض التابعة في نفس مستوى التداخل. بدلاً من وضع العنصر A في LinearLayout، و LinearLayout في RelativeLayout، يتم ربط جميع العناصر مباشرة بـ ConstraintLayout الأصل أو ببعضها البعض من خلال السمات. يوفر هذا: استهلاكاً أقل للذاكرة (كل ViewGroup هي كائن في Java heap)، وlayout pass أسرع (استدعاءات تكرارية أقل)، وسلوكاً أكثر قابلية للتنبؤ عند تغير أحجام الشاشة.

نظام القيود: الربط والانحياز والهامش

القيد (constraint) هو اتصال بين حافة عرض (أو مركزها) وحافة عرض آخر أو الأصل. يمكن أن يحتوي كل عرض على ما يصل إلى 8 قيود: left و top و right و bottom و start و end و baseline و center. كحد أدنى، يكفي قيدان متعامدان لتحديد الموضع (مثل top + left).

تنسيق السمة: app:layout_constraint[Source]_to[Target]Of="[id]" — حيث Source هي الحافة المرتبطة (Left, Right, Top, Bottom, Start, End, Baseline) و Target هي الحافة الهدف. مثال: app:layout_constraintTop_toBottomOf="@+id/header" يعني "الحافة العلوية للعنصر الحالي مرتبطة بالحافة السفلية لعنصر header". للربط بالأصل، يُستخدم id parent.

الانحياز (Bias) هو معلمة تعمل عند وجود قيود متعاكسة (left + right أو top + bottom). تتراوح القيم من 0 إلى 1: 0 — مضغوط نحو الحافة اليسرى/العليا، 0.5 — في المنتصف، 1 — نحو الحافة اليمنى/السفلى. السمات: layout_constraintHorizontal_bias (0.0–1.0) و layout_constraintVertical_bias. يتم تعيين الهوامش باستخدام android:layout_margin* القياسية، لكن القيود والهوامش تعمل بشكل مستقل: الهامش هو إزاحة من القيد، وليس من العرض المجاور.

تحديد المواقع بالنسبة المئوية

منذ ConstraintLayout 1.1+، تمت إضافة دعم الأحجام المئوية عبر layout_constraintWidth_percent و layout_constraintHeight_percent. القيمة 0.3 تعني 30% من عرض/ارتفاع الأصل. بالاقتران مع الانحياز، يتيح ذلك إنشاء تخطيطات متكيفة دون برمجة.

السلاسل والخطوط الإرشادية

السلسلة (Chain) هي مجموعة من عرضين أو أكثر متصلة بقيود ثنائية الاتجاه (A مرتبط بـ B، B مرتبط بـ A). توزع السلاسل المساحة تلقائياً بين العناصر وفقاً لأحد الأوضاع: spread (بالتساوي مع مراعاة الهوامش)، spread_inside (بالتساوي، العناصر الخارجية بدون هامش حافة)، packed (العناصر مضغوطة معاً بانحياز مشترك). يُحدد الوضع عبر السمة app:layout_constraintHorizontal_chainStyle أو layout_constraintVertical_chainStyle.

الخط الإرشادي (Guideline) هو عرض مساعد، غير مرئي أثناء التشغيل، يحدد خطاً للربط. يمكن أن يكون الخط الإرشادي أفقياً أو عمودياً، موضوعاً بـ dp أو نسب مئوية (app:layout_constraintGuide_percent) أو بإزاحة من الحافة (app:layout_constraintGuide_begin/end). الخطوط الإرشادية لا غنى عنها للتخطيطات المتكيفة — على سبيل المثال، لتقسيم الشاشة إلى نصفين متساويين بغض النظر عن حجم الجهاز.

وفقاً لـ Google I/O 2017، السلاسل مع spread_inside أكثر كفاءة بنسبة 15–20% من LinearLayouts المتداخلة مع weight، لأنها تتجنب تمريرة measure المزدوجة اللازمة لحساب weight.

Barrier و Group والمساعدات الافتراضية

Barrier (الحاجز) هو عرض افتراضي يضبط موضعه ديناميكياً بناءً على حجم مجموعة من العناصر. على عكس Guideline ذو الموضع الثابت، يتم "دفع" الحاجز بواسطة أوسع عنصر في المجموعة. على سبيل المثال، إذا كان لديك عنوان ووصف بأطوال غير معروفة، فإن Barrier مرتبط بالحافة اليمنى لأوسع نص يتيح وضع أيقونة بعده مباشرة. السمات: app:barrierDirection (left, right, top, bottom, start, end) و app:constraint_referenced_ids (قائمة ids مفصولة بفواصل).

Group هو حاوية افتراضية تدير رؤية عروض متعددة في وقت واحد. بدلاً من استدعاء setVisibility لكل عنصر على حدة، يكفي تغيير رؤية Group واحد. Group لا يؤثر على تحديد المواقع — فقط على الرؤية. Flow هو مساعد افتراضي لإنشاء تخطيطات "متدفقة": تنتقل العناصر تلقائياً إلى صف/عمود جديد عند نفاد المساحة، مثل النص في فقرة. يدعم Flow wrapMode: none و chain و aligned.

تسمى هذه الأدوات (Barrier و Group و Flow و Guideline) بالمساعدات الافتراضية لأنها ليست عروضاً بالمعنى التقليدي — فهي لا تشغل مساحة في التسلسل الهرمي ولا تشارك في أحداث التركيز أو اللمس. الغرض منها هو تبسيط صيانة التخطيطات المعقدة دون إضافة حاويات متداخلة.

أمثلة: XML و Kotlin

مثال 1: نموذج أساسي مع قيود

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

xml
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/email_input"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="32dp"
        android:layout_marginHorizontal="16dp" />

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/password_input"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/email_input"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="16dp"
        android:layout_marginHorizontal="16dp" />

    <Button
        android:id="@+id/login_button"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toBottomOf="@+id/password_input"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_marginTop="24dp"
        android:layout_marginHorizontal="16dp"
        android:text="تسجيل الدخول" />

</androidx.constraintlayout.widget.ConstraintLayout>

جميع العناصر بعرض 0dp (match_constraint)، مما يعني أنها تتمدد من قيد start إلى end مع مراعاة الهوامش الأفقية. هذا يعادل match_parent مع هوامش، ولكن بدون تداخل.

مثال 2: سلسلة مع spread_inside

ثلاثة أزرار موزعة بالتساوي أفقياً مع هوامش حافة. تضع سلسلة spread_inside الأزرار الخارجية عند الحواف والزر الأوسط في المنتصف بينهما.

xml
<Button
    android:id="@+id/btn_left"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@+id/btn_center"
    android:text="يسار" />

<Button
    android:id="@+id/btn_center"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn_left"
    app:layout_constraintRight_toLeftOf="@+id/btn_right"
    android:text="وسط" />

<Button
    android:id="@+id/btn_right"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    app:layout_constraintLeft_toRightOf="@+id/btn_center"
    app:layout_constraintRight_toRightOf="parent"
    android:text="يمين" />

يتم إنشاء السلسلة تلقائياً عندما تحتوي العناصر على قيود ثنائية الاتجاه. يتم تعيين وضع spread_inside على أي عنصر من السلسلة عبر app:layout_constraintHorizontal_chainStyle="spread_inside". هذا يلغي الحاجة إلى LinearLayout مع weightSum و layout_weight.

مثال 3: Guideline لتخطيط متماثل

إنشاء عمودين متساويين باستخدام Guideline عمودية بنسبة 50%. العنصر الأيسر مرتبط بالأصل الأيسر وحافته اليمنى بالخط الإرشادي؛ العنصر الأيمن مرتبط بحافته اليسرى بالخط الإرشادي وبالأصل الأيمن.

xml
<androidx.constraintlayout.widget.Guideline
    android:id="@+id/gl_midpoint"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:orientation="vertical"
    app:layout_constraintGuide_percent="0.5" />

<TextView
    android:id="@+id/left_card"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toLeftOf="parent"
    app:layout_constraintRight_toLeftOf="@+id/gl_midpoint"
    android:layout_margin="8dp"
    android:background="@color/card_background" />

<TextView
    android:id="@+id/right_card"
    android:layout_width="0dp"
    android:layout_height="0dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintLeft_toRightOf="@+id/gl_midpoint"
    app:layout_constraintRight_toRightOf="parent"
    android:layout_margin="8dp"
    android:background="@color/card_background" />

Guideline بنسبة 0.5 تتكيف تلقائياً مع عرض الشاشة. على كل من الجهاز اللوحي والهاتف، تبقى نسبة الأعمدة 50/50. للتسمية يسار/يمين، استخدم سمات start/end للتوافق مع RTL.

مقارنة: ConstraintLayout مقابل LinearLayout مقابل RelativeLayout

جدول مقارنة لثلاث ViewGroups رئيسية لتطوير Android: ConstraintLayout و LinearLayout و RelativeLayout. المعايير: المرونة والأداء وتعقيد الكود وحالات الاستخدام.

الخاصيةConstraintLayoutLinearLayoutRelativeLayout
التداخلمسطح (مستوى واحد)يتطلب تداخلاً للتخطيطات المعقدةمستوى واحد لكن مرونة محدودة
أداء measureتمريرتان (~40% أسرع)4+ تمريرات مع weightتمريرتان
أحجام مئويةنعم (guide_percent, width_percent)فقط عبر weight/frameلا
دعم RTLمدمج (start/end)مدمجعبر start/end (API 17+)
Barrier/Group/Flowنعم (مساعدات افتراضية)لالا
رسوم متحركة MotionLayoutنعملالا
متى يُستخدمجميع التخطيطات المعقدة، شاشات >5 عناصرقوائم أحادية الاتجاه بسيطة، صفوف بأزرارتخطيطات نسبية بسيطة (كود قديم)

وفقاً لـ Android Vitals (Google, 2025)، التطبيقات التي تستخدم ConstraintLayout كحاوية رئيسية تظهر في المتوسط 18% أقل من إطارات jank عند عرض شاشات معقدة مقارنة بالتطبيقات التي تستخدم LinearLayouts متداخلة. في IT Sectr، انتقلنا إلى ConstraintLayout كمعيار لجميع تخطيطات XML في 2018 — مما قلل متوسط عمق التسلسل الهرمي للشاشة من 4.2 إلى 1.8 مستوى وسرّع تطوير النماذج الجديدة بنسبة 25%.

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

ما الفرق بين match_parent و 0dp (match_constraint) في ConstraintLayout؟

match_parent في ConstraintLayout يعمل كالمعتاد — يمد العرض إلى حجم الأصل. 0dp (match_constraint) يعني أن حجم العرض يُحسب من القيود: إذا تم تعيين قيود left و right مع هوامش، العرض = parent — marginLeft — marginRight. الفرق في السلوك: match_parent يتجاهل الانحياز وقد يتجاوز الحدود أثناء الحركة؛ match_constraint يحترم جميع القيود بشكل صحيح وتوصي به Google كوضع رئيسي لـ ConstraintLayout.

كيفية إنشاء تخطيط متكيف للأجهزة اللوحية باستخدام ConstraintLayout؟

استخدم مزيجاً من: الأحجام المئوية (layout_constraintWidth_percent) للعناصر التي يجب أن تشغل حصة من الشاشة؛ Guideline بنسب مئوية لتقسيم الشاشة إلى مناطق؛ Barrier لتحديد الموضع بالنسبة للمحتوى الديناميكي؛ Flow مع wrapMode لتفاف البطاقات إلى صف جديد. نهج بديل هو استخدام SlidingPaneLayout مع ConstraintLayout لواجهات master-detail على الأجهزة اللوحية.

هل يمكن استخدام ConstraintLayout في Jetpack Compose؟

Jetpack Compose لا يستخدم ConstraintLayout كـ ViewGroup، لكنه يوفر إصدار ConstraintLayout لـ compose (androidx.constraintlayout:constraintlayout-compose) بنفس API في Kotlin DSL: createRefFor() و constrainAs() و linkTo() و chain() و guideFrom(). هذا مفيد للتخطيطات المعقدة التي يسهل وصفها عبر القيود بدلاً من Column/Row. ومع ذلك، في Compose، يُوصى بالبدء بـ Column/Row/Box والانتقال إلى ConstraintLayout فقط عند الحاجة لتحديد مواضع نسبية معقدة.

كيفية تصحيح تداخل العناصر في ConstraintLayout؟

في Android Studio، افتح Layout Inspector (Tools → Layout Inspector)، اختر التطبيق قيد التشغيل ومرر فوق العنصر المشكل. سترى جميع القيود والهوامش والحشو والانحياز في تمثيل ثلاثي الأبعاد. لـ XML، استخدم لوحة Design في محرر التخطيطات — يبرز تعارضات القيود باللون الأصفر والقيود المفقودة باللون الأحمر. في الكود، تأكد من أن كل عرض لديه قيدان متعامدان، وإلا سينتهي العنصر عند (0,0).

الملخص

  • ConstraintLayout — ViewGroup لتخطيطات Android ذات التسلسل الهرمي المسطح عبر قيود تصريحية، مما يقلل عمق التداخل إلى مستوى واحد.
  • نظام القيود — ربط الحواف بالأصل أو عروض أخرى، وانحياز للإزاحة، وأحجام مئوية للتكيف.
  • السلاسل — سلاسل بأوضاع spread / spread_inside / packed لتوزيع العناصر بدون حاويات متداخلة.
  • Guideline و Barrier — خطوط إرشادية ثابتة ونسبية، وحواجز ديناميكية لتحديد مواضع مرن.
  • المساعدات الافتراضية — Group (إدارة الرؤية)، Flow (التفاف تلقائي)، MotionLayout (رسوم متحركة).
  • الأداء — أسرع بنسبة 40% من LinearLayouts المتداخلة، و18% أقل من إطارات jank.
  • الاختيار — ConstraintLayout للشاشات المعقدة، LinearLayout للصفوف البسيطة، RelativeLayout فقط في الكود القديم.

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

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

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

اقرأ أيضًا