ما بررسی میکنیم که ConstraintLayout چیست — یک سیستم موقعیتیابی منعطف برای Android که به شما امکان میدهد سلسلهمراتب تخت viewها را با استفاده از محدودیتها (constraints) به جای LinearLayout و RelativeLayout تو در تو بسازید. ConstraintLayout مشکل «تودرتوی جهنمی» layout (layout nesting hell) را حل میکند، عمق سلسلهمراتب را به یک سطح کاهش میدهد و رندر صفحه را سرعت میبخشد. این کتابخانه بخشی از Jetpack است و از Android 2.3 (API 9) به بعد از طریق support-library در دسترس است. مکانیزمهای اصلی در مستندات رسمی Android توضیح داده شدهاند.
نکات اصلی
ConstraintLayout — یک ViewGroup از کتابخانه AndroidX ConstraintLayout است که برای ایجاد رابطهای منعطف و کارآمد از طریق محدودیتهای اعلامی (constraints) طراحی شده است. برخلاف LinearLayout که عناصر را در یک خط قرار میدهد یا RelativeLayout که نسبت به همسایگان موقعیتیابی میکند، ConstraintLayout به شما امکان میدهد هر عنصر را همزمان نسبت به هر عنصر دیگر و والد متصل کنید.
این کتابخانه در Google I/O 2016 به عنوان راهحلی برای تسریع رندر صفحات پیچیده معرفی شد. مشکل کلیدی که ConstraintLayout حل میکند تودرتوی layout است. هر ViewGroup تو در تو حداقل دو بار measure و یک بار layout اضافه میکند. صفحهای با ۴ سطح تودرتو ۸ بار measure اجرا میکند؛ ConstraintLayout با همان قابلیت — فقط ۲ بار بر اساس دادههای Google (Android Performance Blog, 2017)، جایگزینی سه LinearLayout تو در تو با یک ConstraintLayout زمان onMeasure را ۴۰٪ کاهش میدهد.
نسخه فعلی ConstraintLayout 2.1.4 به طور پایدار روی Android 2.3+ (API 9) از طریق AndroidX کار میکند. در نسخه ۲.۰، موقعیتیابی دایرهای، Flow (انتقال خودکار عناصر) و پشتیبانی MotionLayout اضافه شد. ConstraintLayout برای درک توسعه مدرن Android ضروری است — در Jetpack Compose به عنوان مفهوم اصلی اصلاحکنندهها، در قالبهای پیشفرض Android Studio و در Material Design 3 استفاده میشود.
سلسلهمراتب تخت ConstraintLayout به این معنی است که همه Viewهای فرزند در یک سطح تودرتو قرار دارند. به جای قرار دادن عنصر A در LinearLayout و سپس LinearLayout در RelativeLayout، همه عناصر مستقیماً به ConstraintLayout والد یا به یکدیگر از طریق ویژگیها متصل میشوند. این مزایا را به همراه دارد: مصرف حافظه کمتر (هر ViewGroup یک شیء در Java heap است)، سرعت بیشتر layout pass (فراخوانیهای بازگشتی کمتر)، رفتار قابل پیشبینیتر در تغییر اندازه صفحه.
محدودیت — ارتباط بین لبه یک View (یا مرکز آن) و لبه View دیگر یا والد است. هر View میتواند تا ۸ محدودیت داشته باشد: چپ، بالا، راست، پایین، start، end، baseline و مرکز. حداقل برای موقعیتیابی دو محدودیت عمود بر هم کافی است (مثلاً بالا + چپ).
فرمت ویژگی: app:layout_constraint[منبع]_to[هدف]Of="[id]" — که منبع لبه متصلشونده است (Left, Right, Top, Bottom, Start, End, Baseline)، هدف لبه مقصد است. مثال: app:layout_constraintTop_toBottomOf="@+id/header" به معنای «مرز بالایی عنصر فعلی به مرز پایینی عنصر header متصل است». برای اتصال به والد از id parent استفاده میشود.
Bias (انحراف) — پارامتری که با وجود محدودیتهای متضاد (left + right یا top + bottom) کار میکند. مقدار از ۰ تا ۱: ۰ — به لبه چپ/بالا چسبیده، ۰.۵ — در مرکز، ۱ — به لبه راست/پایین. ویژگیها: layout_constraintHorizontal_bias (۰.۰–۱.۰) و layout_constraintVertical_bias. حاشیه با android:layout_margin* استاندارد تعیین میشود، اما محدودیت و حاشیه مستقل عمل میکنند: حاشیه فاصله از محدودیت است، نه از View مجاور.
از ConstraintLayout 1.1+ به بعد، اندازههای درصدی از طریق layout_constraintWidth_percent و layout_constraintHeight_percent پشتیبانی میشوند. مقدار ۰.۳ به معنای ۳۰٪ از عرض/ارتفاع والد است. در ترکیب با bias، این امکان ایجاد layoutهای تطبیقی بدون برنامهنویسی را فراهم میکند.
Chain (زنجیره) — گروهی از دو یا چند View است که با محدودیتهای دوطرفه متصل شدهاند (A به B و B به A). زنجیرهها فضا را بین عناصر به طور خودکار در یکی از حالتهای زیر توزیع میکنند: spread (مساوی با در نظر گرفتن حاشیه)، spread_inside (مساوی، عناصر کناری بدون فاصله از لبهها)، packed (عناصر به هم چسبیده با bias مشترک). حالت با ویژگی app:layout_constraintHorizontal_chainStyle یا layout_constraintVertical_chainStyle تعیین میشود.
Guideline (راهنما) — یک View کمکی است که در زمان اجرا نامرئی است اما خطی را برای اتصال تعیین میکند. Guideline میتواند افقی یا عمودی باشد، با موقعیتیابی در dp، درصد (app:layout_constraintGuide_percent) یا با فاصله از لبه (app:layout_constraintGuide_begin/end). راهنماها برای layoutهای تطبیقی ضروری هستند — مثلاً برای تقسیم صفحه به دو نیمه مساوی بدون توجه به اندازه دستگاه.
بر اساس دادههای Google I/O 2017، زنجیرههای spread_inside ۱۵–۲۰٪ کارآمدتر از LinearLayoutهای تو در تو با weight هستند، زیرا از اندازهگیری مضاعف measure که برای محاسبه weight لازم است جلوگیری میکنند.
Barrier (مانع) — یک View مجازی است که به طور پویا موقعیت خود را با اندازه گروه عناصر تنظیم میکند. برخلاف Guideline با موقعیت ثابت، Barrier توسط عریضترین عنصر گروه «هل داده میشود». مثلاً اگر عنوان و توضیحی با طول نامشخص دارید، Barrier متصل به لبه راست عریضترین متن به شما امکان میدهد آیکون را بلافاصله بعد از آنها قرار دهید. ویژگیها: app:barrierDirection (left, right, top, bottom, start, end) و app:constraint_referenced_ids (لیست idهای جدا شده با کاما).
Group — یک کانتینر مجازی است که قابلیت مشاهده چندین View را به طور همزمان مدیریت میکند. به جای فراخوانی setVisibility برای هر عنصر به طور جداگانه، کافی است قابلیت مشاهده یک Group را تغییر دهید. Group روی موقعیتیابی تأثیر نمیگذارد — فقط روی قابلیت مشاهده. Flow — یک کمککننده مجازی برای ایجاد layoutهای «جاری»: عناصر در صورت کمبود فضا به طور خودکار به سطر/ستون جدید منتقل میشوند، مانند متن در یک پاراگراف. Flow از wrapMode: none، chain و aligned پشتیبانی میکند.
این ابزارها (Barrier, Group, Flow, Guideline) کمککنندههای مجازی نامیده میشوند زیرا در معنای کلاسیک View نیستند — در سلسلهمراتب جا نمیگیرند و در فوکوس یا رویدادهای لمسی شرکت نمیکنند. هدف آنها سادهسازی نگهداری layoutهای پیچیده بدون افزودن کانتینرهای تو در تو است.
فرم ورود ساده با فیلد ایمیل، رمز عبور و دکمه. همه عناصر به جز دکمه به parent متصل هستند — دکمه زیر فیلد رمز عبور قرار دارد. از سلسلهمراتب تخت استفاده شده است — هر سه عنصر در یک سطح.
<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>
همه عناصر عرض ۰dp (match_constraint) دارند، یعنی از start تا end محدودیت با در نظر گرفتن حاشیه افقی کشیده میشوند. این معادل match_parent با حاشیه است، اما بدون تودرتو.
سه دکمه که به طور مساوی در افق با فاصله از لبهها توزیع شدهاند. زنجیره spread_inside دکمههای کناری را در لبهها و دکمه وسط را در مرکز بین آنها قرار میدهد.
<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 را از بین میبرد.
ایجاد دو ستون مساوی از طریق Guideline عمودی روی ۵۰٪. عنصر چپ به لبه چپ parent و با لبه راست به guideline متصل است؛ عنصر راست — با لبه چپ به guideline و به لبه راست parent.
<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 با درصد ۰.۵ به طور خودکار با عرض صفحه تنظیم میشود. در تبلت و تلفن، نسبت ستونها ۵۰/۵۰ باقی میماند. برای نامگذاری چپ/راست از ویژگیهای start/end برای سازگاری RTL استفاده کنید.
جدول مقایسه سه ViewGroup اصلی برای توسعه Android: ConstraintLayout، LinearLayout و RelativeLayout. معیارها: انعطافپذیری، کارایی، پیچیدگی کد و حوزههای کاربرد.
| ویژگی | ConstraintLayout | LinearLayout | RelativeLayout |
|---|---|---|---|
| تودرتو | تخت (یک سطح) | برای layoutهای پیچیده نیاز به تودرتو دارد | یک سطح اما انعطاف محدود |
| کارایی measure | ۲ بار (~۴۰٪ سریعتر) | ۴+ بار با weight | ۲ بار |
| اندازههای درصدی | بله (guide_percent, width_percent) | فقط از طریق weight/frame | خیر |
| پشتیبانی RTL | داخلی (start/end) | داخلی | از طریق start/end (API 17+) |
| Barrier/Group/Flow | بله (کمککنندههای مجازی) | خیر | خیر |
| انیمیشن MotionLayout | بله | خیر | خیر |
| زمان استفاده | همه layoutهای پیچیده، صفحههای با >۵ عنصر | لیستهای ساده یکجهته، ردیفهای دکمه | layoutهای نسبی ساده (از کد قدیمی) |
بر اساس دادههای Android Vitals (Google, 2025)، برنامههایی که از ConstraintLayout به عنوان کانتینر اصلی استفاده میکنند، به طور متوسط ۱۸٪ فریمهای jank کمتری در رندر صفحات پیچیده نسبت به برنامههای با LinearLayoutهای تو در تو نشان میدهند. در IT Sectr ما در سال ۲۰۱۸ به ConstraintLayout به عنوان استاندارد برای همه layoutهای XML تغییر مکان دادیم — این کار میانگین عمق سلسلهمراتب صفحه را از ۴.۲ به ۱.۸ سطح کاهش داد و توسعه فرمهای جدید را ۲۵٪ سرعت بخشید.
سوالات متداول
match_parent در ConstraintLayout مانند معمول کار میکند — View را تا اندازه والد میکشد. ۰dp (match_constraint) به این معنی است که اندازه View از محدودیتها محاسبه میشود: اگر محدودیتهای چپ و راست با حاشیه تنظیم شده باشند، عرض = parent — marginLeft — marginRight. تفاوت در رفتار: match_parent bias را نادیده میگیرد و ممکن است در هنگام انیمیشن از مرزها خارج شود؛ match_constraint به درستی همه محدودیتها را در نظر میگیرد و توسط Google به عنوان حالت اصلی برای ConstraintLayout توصیه میشود.
از ترکیب زیر استفاده کنید: اندازههای درصدی (layout_constraintWidth_percent) برای عناصری که باید بخشی از صفحه را اشغال کنند؛ Guideline با درصد برای تقسیم صفحه به مناطق؛ Barrier برای موقعیتیابی نسبت به محتوای پویا؛ Flow با wrapMode برای انتقال کارتها به سطر جدید. رویکرد جایگزین — استفاده از SlidingPaneLayout در ترکیب با ConstraintLayout برای رابطهای master-detail در تبلتها.
Jetpack Compose از ConstraintLayout به عنوان ViewGroup استفاده نمیکند، اما نسخه Compose ConstraintLayout (androidx.constraintlayout:constraintlayout-compose) را با همان API در Kotlin DSL ارائه میدهد: createRefFor()، constrainAs()، linkTo()، chain()، guideFrom(). این برای layoutهای پیچیدهای که توصیف آنها از طریق Column/Row دشوارتر است مفید میباشد. با این حال، در Compose توصیه میشود با Column/Row/Box شروع کنید و فقط زمانی که به موقعیتیابیهای نسبی پیچیده نیاز دارید به ConstraintLayout بروید.
در Android Studio Layout Inspector را باز کنید (Tools → Layout Inspector)، برنامه در حال اجرا را انتخاب کنید و روی عنصر مشکلدار بروید. همه محدودیتها، حاشیهها، padding و bias را در نمای سهبعدی خواهید دید. برای XML از پنل Design در ویرایشگر layout استفاده کنید — تداخل محدودیتها را زرد و عدم وجود محدودیت را قرمز نشان میدهد. در کد بررسی کنید که هر View دو محدودیت عمود بر هم دارد، در غیر این صورت عنصر در (۰,۰) قرار میگیرد.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید