ConstraintLayout — що це таке, констрейнти та Flat Hierarchy

Автор: IT Sectr Опубліковано: 2026-02-24 Час читання: 10 хв

Розбираємося, що таке ConstraintLayout — гнучка система позиціонування для Android, яка дозволяє будувати пласкі ієрархії в'ю за допомогою констрейнтів (обмежень) замість вкладених 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 — ланцюги розподіляють елементи рівномірно або за вагою; напрямні (Guideline) задають пропорційні відступи у відсотках.
  • Barrier та Group — бар'єр динамічно підлаштовується під розмір групи елементів; Group керує видимістю кількох в'ю одночасно.
  • MotionLayout — підклас ConstraintLayout для анімації переходів між станами констрейнтів з підтримкою KeyFrame.

Що таке ConstraintLayout?

ConstraintLayout — це ViewGroup з бібліотеки AndroidX ConstraintLayout, призначена для створення гнучких та продуктивних інтерфейсів через декларативні обмеження (constraints). На відміну від LinearLayout, який вибудовує елементи в одну лінію, або RelativeLayout, який позиціонує відносно сусідів, ConstraintLayout дозволяє закріпити кожен елемент відносно будь-яких інших елементів та батька одночасно.

Бібліотека була анонсована на Google I/O 2016 як рішення для прискорення рендерингу складних екранів. Ключова проблема, яку вирішує ConstraintLayout — вкладеність макетів. Кожен вкладений ViewGroup додає мінімум два проходи measure і один прохід layout. Екран з 4 рівнями вкладеності виконує 8 проходів measure; ConstraintLayout з тією ж функціональністю — всього 2 проходи. За даними Google (Android Performance Blog, 2017), заміна трьох вкладених LinearLayout на один 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 означає, що всі дочірні View знаходяться на одному рівні вкладеності. Замість того щоб поміщати елемент A в LinearLayout, а LinearLayout — в RelativeLayout, всі елементи прив'язуються безпосередньо до батьківського ConstraintLayout або один до одного через атрибути. Це дає: менше споживання пам'яті (кожен ViewGroup — об'єкт у Java heap), прискорення layout pass (менше рекурсивних викликів), більш передбачувану поведінку при зміні розмірів екрана.

Система констрейнтів: прив'язки, bias та margin

Констрейнт (обмеження) — це зв'язок між краєм одного View (або його центром) та краєм іншого View або батька. Кожен View може мати до 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. Margin задається стандартними android:layout_margin*, але констрейнт та margin працюють незалежно: margin — це відступ від констрейнта, а не від сусіднього View.

Відсоткове позиціонування

З ConstraintLayout 1.1+ з'явилася підтримка відсоткових розмірів через layout_constraintWidth_percent та layout_constraintHeight_percent. Значення 0.3 означає 30% від ширини/висоти батька. У поєднанні з bias це дозволяє створювати адаптивні макети без програмування.

Chain та Guideline

Chain (ланцюг) — це група з двох або більше View, пов'язаних двосторонніми констрейнтами (A прив'язаний до B, B до A). Ланцюги автоматично розподіляють простір між елементами за одним із режимів: spread (рівномірно, з урахуванням margin), spread_inside (рівномірно, крайові елементи без відступу від країв), packed (елементи притиснуті один до одного із загальним bias). Режим задається атрибутом app:layout_constraintHorizontal_chainStyle або layout_constraintVertical_chainStyle.

Guideline (напрямна) — допоміжний View, невидимий у рантаймі, що задає лінію для прив'язки. Guideline може бути горизонтальним або вертикальним, позиціонованим у dp, відсотках (app:layout_constraintGuide_percent) або з відступом від краю (app:layout_constraintGuide_begin/end). Guidelines незамінні для адаптивних макетів — наприклад, для поділу екрана на дві рівні половини незалежно від розміру пристрою.

За даними Google I/O 2017, ланцюги з spread_inside на 15–20% продуктивніші за вкладені LinearLayout з weight, оскільки уникають подвійного проходу measure, необхідного для розрахунку weight.

Barrier, Group та віртуальні помічники

Barrier (бар'єр) — віртуальний View, який динамічно підлаштовує свою позицію під розмір групи елементів. На відміну від Guideline з фіксованою позицією, Barrier «штовхається» найширшим елементом групи. Наприклад, якщо у вас є заголовок та опис невідомої довжини, Barrier, прив'язаний до правого краю найширшого тексту, дозволяє розмістити іконку одразу після них. Атрибути: app:barrierDirection (left, right, top, bottom, start, end) та app:constraint_referenced_ids (список id через кому).

Group — віртуальний контейнер, який керує видимістю (visibility) кількох View одночасно. Замість того щоб викликати setVisibility для кожного елемента окремо, достатньо змінити visibility однієї Group. Group не впливає на позиціонування — лише на видимість. Flow — віртуальний помічник для створення «плинних» макетів: елементи автоматично переносяться на новий рядок/колонку при нестачі місця, як текст в абзаці. Flow підтримує wrapMode: none, chain та aligned.

Ці інструменти (Barrier, Group, Flow, Guideline) називаються віртуальними помічниками, тому що вони не є View у класичному сенсі — вони не займають місця в ієрархії та не беруть участі у фокусі чи тач-подіях. Їхня мета — спростити підтримку складних макетів без додавання вкладених контейнерів.

Приклади: XML та Kotlin

Приклад 1: Базова форма з констрейнтами

Проста форма входу з полем email, паролем та кнопкою. Всі елементи прив'язані до parent, крім кнопки — вона під полем пароля. Використовується flat hierarchy — всі три елементи на одному рівні.

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 констрейнта з урахуванням horizontal margin. Це аналог 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%. Лівий елемент прив'язаний до left parent і правим краєм до guideline; правий — лівим краєм до guideline і до right parent.

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 vs LinearLayout vs RelativeLayout

Таблиця порівняння трьох основних ViewGroup для Android-розробки: ConstraintLayout, LinearLayout та RelativeLayout. Критерії: гнучкість, продуктивність, складність коду та області застосування.

ХарактеристикаConstraintLayoutLinearLayoutRelativeLayout
ВкладеністьПласка (один рівень)Вимагає вкладеності для складних макетівОдин рівень, але обмежена гнучкість
Продуктивність measure2 проходи (~40% швидше)4+ проходи при weight2 проходи
Відсоткові розміриТак (guide_percent, width_percent)Тільки через weight/frameНі
RTL-підтримкаВбудована (start/end)ВбудованаЧерез start/end (API 17+)
Barrier/Group/FlowТак (віртуальні помічники)НіНі
MotionLayout анімаціїТакНіНі
Коли використовуватиВсі складні макети, екрани з >5 елементамиПрості однонаправлені списки, рядки з кнопкамиПрості відносні макети (з Legacy-коду)

За даними Android Vitals (Google, 2025), додатки, що використовують ConstraintLayout як основний контейнер, в середньому показують на 18% менше jank-кадрів при відображенні складних екранів порівняно з додатками на вкладених LinearLayout. В IT Sectr ми перейшли на ConstraintLayout як стандарт для всіх XML-макетів у 2018 році — це скоротило середню глибину ієрархії екранів з 4.2 до 1.8 рівня та прискорило розробку нових форм на 25%.

Часто задавані питання

В чому різниця між match_parent та 0dp (match_constraint) у ConstraintLayout?

match_parent у ConstraintLayout працює як зазвичай — розтягує View до розміру батька. 0dp (match_constraint) означає, що розмір View обчислюється з констрейнтів: якщо задані left та right констрейти з margin, ширина = parent — marginLeft — marginRight. Різниця в поведінці: match_parent ігнорує bias і може вилізти за межі при анімації; 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), виберіть запущений додаток і наведіть на проблемний елемент. Ви побачите всі констрейти, margin, padding та bias у тривимірному представленні. Для XML використовуйте панель Design у редакторі макетів — вона підсвічує конфлікти констрейнтів жовтим, а відсутність констрейнтів — червоним. У коді перевірте, що кожен View має два перпендикулярних констрейти, інакше елемент опиниться в (0,0).

Підсумки

  • ConstraintLayout — ViewGroup для пласкої ієрархії Android-макетів через декларативні констрейти, що скорочує глибину вкладеності до 1 рівня.
  • Система констрейнтів — прив'язка країв до parent або інших View, bias для зміщення, відсоткові розміри для адаптивності.
  • Chains — ланцюги з режимами spread / spread_inside / packed для розподілу елементів без вкладених контейнерів.
  • Guideline та Barrier — напрямні (фіксовані та відсоткові) та динамічні бар'єри для гнучкого позиціонування.
  • Віртуальні помічники — Group (управління видимістю), Flow (автоматичне перенесення), MotionLayout (анімація).
  • Продуктивність — на 40% швидше вкладених LinearLayout, на 18% менше jank-кадрів.
  • Вибір — ConstraintLayout для складних екранів, LinearLayout для простих рядків, RelativeLayout тільки в legacy-коді.

Ми розробимо мобільний застосунок під ключ

IT Sectr створює застосунки для iOS та Android для стартапів і бізнесу з 2017 року. Ми проконсультуємо вас і запропонуємо найкраще рішення.

Обговорити проект

Читайте також